diff --git a/apps/sim/app/api/v2/chat/route.test.ts b/apps/sim/app/api/v2/chat/route.test.ts index 4fb03ebb53c..81010d6800c 100644 --- a/apps/sim/app/api/v2/chat/route.test.ts +++ b/apps/sim/app/api/v2/chat/route.test.ts @@ -84,7 +84,7 @@ vi.mock('@/lib/mothership/chat/messages-store', () => mothershipChatMessagesMock vi.mock('@/lib/mothership/chat/payload', () => mothershipChatPayloadMock) vi.mock('@/lib/mothership/entitlements', () => ({ - computeWorkspaceEntitlements: vi.fn().mockResolvedValue([]), + computeEntitlements: vi.fn().mockResolvedValue([]), })) vi.mock('@/lib/mothership/request/lifecycle/headless', () => mothershipHeadlessLifecycleMock) @@ -417,8 +417,9 @@ describe('POST /api/v2/chat', () => { workspaceId: 'workspace-1', chatId: SERVER_ISSUED_CHAT_ID, integrationCatalog: { mcpServerIds: [] }, + mode: 'agent', }) - for (const legacy of ['messages', 'mode', 'isHosted', 'workspaceContext', 'userPermission']) { + for (const legacy of ['messages', 'isHosted', 'workspaceContext', 'userPermission']) { expect(payload).not.toHaveProperty(legacy) } expect(options).toMatchObject({ diff --git a/apps/sim/app/api/v2/chat/route.ts b/apps/sim/app/api/v2/chat/route.ts index 46b9beaa742..29b9297ba93 100644 --- a/apps/sim/app/api/v2/chat/route.ts +++ b/apps/sim/app/api/v2/chat/route.ts @@ -36,6 +36,7 @@ import { buildPersistedUserMessage, } from '@/lib/mothership/chat/persisted-message' import { MOTHERSHIP_CHAT_DEFAULT_MODEL } from '@/lib/mothership/constants' +import { computeEntitlements } from '@/lib/mothership/entitlements' import { type CopilotEnvironmentContext, createCopilotEnvironmentContext, @@ -343,6 +344,8 @@ export const POST = withRouteHandler( chatId, messageId, integrationCatalog: { mcpServerIds: [] }, + mode: 'agent', + entitlements: await computeEntitlements({ principal, workspaceId }), ...(effort ? { effort } : {}), } diff --git a/apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts b/apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts new file mode 100644 index 00000000000..cb48afe6924 --- /dev/null +++ b/apps/sim/app/api/workspaces/[id]/tests/[name]/route.ts @@ -0,0 +1,27 @@ +import { getWorkflowTestContract } from '@/lib/api/contracts/workflow-tests' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { getWorkflowTestDetail } from '@/lib/workflow-tests/application/tests' + +export const GET = defineInternalJsonRoute({ + contract: getWorkflowTestContract, + auth: internalSessionAuth, + operation: workflowTestOperations.read, + rateLimit: internalRateLimits.user({ + bucketName: 'workflow-tests-read', + // A running test polls these every 3s from each open page and chat tab. + config: { maxTokens: 240, refillRate: 120, refillIntervalMs: 60_000 }, + }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params, query }) => ({ + workspaceId: params.id, + name: params.name, + version: query.version, + }), + useCase: getWorkflowTestDetail, +}) diff --git a/apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts b/apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts new file mode 100644 index 00000000000..b9c82caedbb --- /dev/null +++ b/apps/sim/app/api/workspaces/[id]/tests/[name]/runs/[runId]/route.ts @@ -0,0 +1,27 @@ +import { getWorkflowTestRunContract } from '@/lib/api/contracts/workflow-tests' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { getWorkflowTestRunDetail } from '@/lib/workflow-tests/application/tests' + +export const GET = defineInternalJsonRoute({ + contract: getWorkflowTestRunContract, + auth: internalSessionAuth, + operation: workflowTestOperations.read, + rateLimit: internalRateLimits.user({ + bucketName: 'workflow-tests-read', + // A running test polls these every 3s from each open page and chat tab. + config: { maxTokens: 240, refillRate: 120, refillIntervalMs: 60_000 }, + }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params }) => ({ + workspaceId: params.id, + name: params.name, + runId: params.runId, + }), + useCase: getWorkflowTestRunDetail, +}) diff --git a/apps/sim/app/api/workspaces/[id]/tests/route.ts b/apps/sim/app/api/workspaces/[id]/tests/route.ts new file mode 100644 index 00000000000..598a6b4fc57 --- /dev/null +++ b/apps/sim/app/api/workspaces/[id]/tests/route.ts @@ -0,0 +1,23 @@ +import { listWorkflowTestsContract } from '@/lib/api/contracts/workflow-tests' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { listWorkflowTests } from '@/lib/workflow-tests/application/tests' + +export const GET = defineInternalJsonRoute({ + contract: listWorkflowTestsContract, + auth: internalSessionAuth, + operation: workflowTestOperations.read, + rateLimit: internalRateLimits.user({ + bucketName: 'workflow-tests-read', + // A running test polls these every 3s from each open page and chat tab. + config: { maxTokens: 240, refillRate: 120, refillIntervalMs: 60_000 }, + }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params, query }) => ({ workspaceId: params.id, version: query.version }), + useCase: listWorkflowTests, +}) diff --git a/apps/sim/app/api/workspaces/[id]/tests/run/route.ts b/apps/sim/app/api/workspaces/[id]/tests/run/route.ts new file mode 100644 index 00000000000..b53971f293a --- /dev/null +++ b/apps/sim/app/api/workspaces/[id]/tests/run/route.ts @@ -0,0 +1,19 @@ +import { runWorkflowTestsContract } from '@/lib/api/contracts/workflow-tests' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { startWorkflowTestRuns } from '@/lib/workflow-tests/application/run-tests' + +export const POST = defineInternalJsonRoute({ + contract: runWorkflowTestsContract, + auth: internalSessionAuth, + operation: workflowTestOperations.run, + rateLimit: internalRateLimits.user({ bucketName: 'workflow-tests' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params, body }) => ({ workspaceId: params.id, ...body }), + useCase: startWorkflowTestRuns, +}) diff --git a/apps/sim/app/o/[organizationId]/home/components/composer/composer.test.tsx b/apps/sim/app/o/[organizationId]/home/components/composer/composer.test.tsx index 059c28598f2..6f40b54ae81 100644 --- a/apps/sim/app/o/[organizationId]/home/components/composer/composer.test.tsx +++ b/apps/sim/app/o/[organizationId]/home/components/composer/composer.test.tsx @@ -232,6 +232,7 @@ async function render( { Promise ) => void + /** Receives why a save was refused; text files only. */ + onSaveError?: (error: unknown) => void + /** Replaces the file's own preview pane, for an owner with a richer view of the file. */ + preview?: ReactNode + /** Which side of a split the source takes; `end` puts it after the preview. */ + sourceSide?: 'start' | 'end' saveRef?: React.MutableRefObject<(() => Promise) | null> downloadSourceRef?: React.MutableRefObject discardRef?: React.MutableRefObject<(() => void) | null> @@ -175,6 +181,9 @@ function FileViewerContent({ autoFocus, onDirtyChange, onSaveStatusChange, + onSaveError, + preview, + sourceSide, saveRef, downloadSourceRef, discardRef, @@ -253,6 +262,9 @@ function FileViewerContent({ autoFocus={autoFocus} onDirtyChange={onDirtyChange} onSaveStatusChange={onSaveStatusChange} + onSaveError={onSaveError} + preview={preview} + sourceSide={sourceSide} saveRef={saveRef} discardRef={discardRef} streamingContent={streamingContent} diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor.tsx index 0dec965bf56..aa2df459009 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/text-editor.tsx @@ -4,6 +4,7 @@ import { memo, type ClipboardEvent as ReactClipboardEvent, type KeyboardEvent as ReactKeyboardEvent, + type ReactNode, useCallback, useEffect, useId, @@ -405,6 +406,9 @@ interface TextEditorProps { status: 'idle' | 'saving' | 'saved' | 'error', retry?: () => Promise ) => void + onSaveError?: (error: unknown) => void + preview?: ReactNode + sourceSide?: 'start' | 'end' saveRef?: React.MutableRefObject<(() => Promise) | null> downloadSourceRef?: React.MutableRefObject discardRef?: React.MutableRefObject<(() => void) | null> @@ -422,6 +426,9 @@ export const TextEditor = memo(function TextEditor({ autoFocus, onDirtyChange, onSaveStatusChange, + onSaveError, + preview, + sourceSide = 'start', saveRef, downloadSourceRef, discardRef, @@ -503,6 +510,7 @@ export const TextEditor = memo(function TextEditor({ isAgentEditing, onDirtyChange, onSaveStatusChange, + onSaveError, saveRef, discardRef, }) @@ -608,7 +616,8 @@ export const TextEditor = memo(function TextEditor({ if (!container) return const rect = container.getBoundingClientRect() const isRtl = getComputedStyle(container).direction === 'rtl' - const sourceWidth = isRtl ? rect.right - e.clientX : e.clientX - rect.left + const fromRight = isRtl !== (sourceSide === 'end') + const sourceWidth = fromRight ? rect.right - e.clientX : e.clientX - rect.left const pct = (sourceWidth / rect.width) * 100 setSplitPct(Math.min(SPLIT_MAX_PCT, Math.max(SPLIT_MIN_PCT, pct))) } @@ -626,7 +635,7 @@ export const TextEditor = memo(function TextEditor({ document.body.style.cursor = '' document.body.style.userSelect = '' } - }, [isResizing]) + }, [isResizing, sourceSide]) const handleSplitKeyDown = (event: ReactKeyboardEvent) => { const key = readSeparatorKey(event) @@ -639,7 +648,8 @@ export const TextEditor = memo(function TextEditor({ } const container = containerRef.current const isRtl = container !== null && getComputedStyle(container).direction === 'rtl' - const delta = (key === 'left' ? -1 : 1) * (isRtl ? -1 : 1) * SPLIT_KEYBOARD_STEP_PCT + const fromRight = isRtl !== (sourceSide === 'end') + const delta = (key === 'left' ? -1 : 1) * (fromRight ? -1 : 1) * SPLIT_KEYBOARD_STEP_PCT setSplitPct((current) => Math.min(SPLIT_MAX_PCT, Math.max(SPLIT_MIN_PCT, current + delta))) } @@ -789,7 +799,10 @@ export const TextEditor = memo(function TextEditor({
{showEditor && ( @@ -847,16 +860,18 @@ export const TextEditor = memo(function TextEditor({ isResizing && 'pointer-events-none' )} > - + {preview ?? ( + + )}
)} diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/use-editable-file-content.ts b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/use-editable-file-content.ts index 06d3d241e89..483094b9ef3 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/use-editable-file-content.ts +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/use-editable-file-content.ts @@ -54,6 +54,8 @@ interface UseEditableFileContentOptions { onDirtyChange?: (isDirty: boolean) => void /** `retry` is this instance's own `saveImmediately`, passed alongside an `'error'` status so a caller-side retry never depends on a shared, remount-able ref. */ onSaveStatusChange?: (status: SaveStatus, retry?: () => Promise) => void + /** Receives why a save was refused, for callers whose server checks the content itself. */ + onSaveError?: (error: unknown) => void saveRef?: React.MutableRefObject<(() => Promise) | null> /** Bridges an imperative "discard the current draft" command up to the caller, mirroring `saveRef`. */ discardRef?: React.MutableRefObject<(() => void) | null> @@ -165,6 +167,7 @@ export function useEditableFileContent({ isAgentEditing, onDirtyChange, onSaveStatusChange, + onSaveError, saveRef, discardRef, normalizeBaseline, @@ -172,6 +175,7 @@ export function useEditableFileContent({ }: UseEditableFileContentOptions): EditableFileContent { const onDirtyChangeRef = useRef(onDirtyChange) const onSaveStatusChangeRef = useRef(onSaveStatusChange) + const onSaveErrorRef = useRef(onSaveError) /** * Mirrors the reducer's `reconciling` phase (assigned below the reducer hook; read here through a @@ -293,6 +297,7 @@ export function useEditableFileContent({ everStreamedRef.current = everStreamed onDirtyChangeRef.current = onDirtyChange onSaveStatusChangeRef.current = onSaveStatusChange + onSaveErrorRef.current = onSaveError updateContentRef.current = updateContent streamActiveRef.current = isAgentStreamActive contentRef.current = content @@ -340,6 +345,7 @@ export function useEditableFileContent({ conflictRef.current = true markConflict() } + onSaveErrorRef.current?.(error) throw error } }, diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources.ts b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources.ts index 70bf34e89cc..404f50cf8c5 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources.ts @@ -17,6 +17,7 @@ import { useKnowledgeBasesQuery } from '@/hooks/queries/kb/knowledge' import { useLogsList } from '@/hooks/queries/logs' import { useMothershipChats } from '@/hooks/queries/mothership-chats' import { useTablesList } from '@/hooks/queries/tables' +import { useWorkflowTests } from '@/hooks/queries/workflow-tests' import { useWorkflows } from '@/hooks/queries/workflows' import { useWorkspaceFileFolders } from '@/hooks/queries/workspace-file-folders' import { useWorkspaceFiles } from '@/hooks/queries/workspace-files' @@ -30,6 +31,12 @@ export const BROWSER_LAUNCHER_ID = 'browser' /** Placeholder id for the Terminal launcher row; the shell the desktop app opens becomes the tab. */ export const TERMINAL_LAUNCHER_ID = 'terminal' +/** + * Families the chat composer cannot attach: they open as resource tabs but have no chat + * context. Sim reads a test through its file, `tests/.test.js`. + */ +export const COMPOSER_EXCLUDED_TYPES: readonly MothershipResourceType[] = ['test'] + export interface AvailableItemsByType { type: MothershipResourceType items: AvailableItem[] @@ -97,6 +104,7 @@ export function useAvailableResources( options?: UseAvailableResourcesOptions ): AvailableResources { const dashboardsEnabled = useFeatureFlag('dashboards') + const testsEnabled = useFeatureFlag('workflow-tests') const enabled = options?.enabled ?? true const excludeTypes = options?.excludeTypes const browserAvailable = useSyncExternalStore( @@ -121,6 +129,9 @@ export function useAvailableResources( const { data: dashboardData, isPending: dashboardsPending } = useWorkspaceDashboard(workspaceId, { enabled: enabled && dashboardsEnabled && !excludeTypes?.includes('dashboard'), }) + const { data: tests, isPending: testsPending } = useWorkflowTests(workspaceId, { + enabled: enabled && testsEnabled && !excludeTypes?.includes('test'), + }) const { data: files, isPending: filesPending } = useWorkspaceFiles(workspaceId, 'active', { enabled: enabled && Boolean(workspaceId), }) @@ -172,6 +183,7 @@ export function useAvailableResources( tablesPending || filesPending || (dashboardsEnabled && !excludeTypes?.includes('dashboard') && dashboardsPending) || + (testsEnabled && !excludeTypes?.includes('test') && testsPending) || knowledgeBasesPending || foldersPending || (options?.includeFolderMentions && @@ -185,6 +197,7 @@ export function useAvailableResources( if (!enabled) return NO_RESOURCE_GROUPS const excluded = new Set(excludeTypes ?? []) if (!dashboardsEnabled) excluded.add('dashboard') + if (!testsEnabled) excluded.add('test') const groups: AvailableItemsByType[] = [ { type: 'workflow' as const, @@ -218,6 +231,10 @@ export function useAvailableResources( ? [{ id: dashboardData.dashboard.id, name: dashboardData.dashboard.name, folderId: null }] : [], }, + { + type: 'test' as const, + items: (tests ?? []).map((test) => ({ id: test.name, name: test.title, folderId: null })), + }, { type: 'file' as const, items: (files ?? []).map((f) => ({ id: f.id, name: f.name, folderId: f.folderId ?? null })), @@ -311,11 +328,13 @@ export function useAvailableResources( tables, files, dashboardData, + tests, knowledgeBases, tasks, logs, excludeTypes, dashboardsEnabled, + testsEnabled, ]) /** diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/index.ts b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/index.ts index 0e9f610be57..5534c34dd3f 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/index.ts @@ -6,5 +6,5 @@ export { useResourceTreeSections, WorkspaceResourceSubmenu, } from './add-resource-dropdown' -export { useAvailableResources } from './available-resources' +export { COMPOSER_EXCLUDED_TYPES, useAvailableResources } from './available-resources' export { resourceFromItem } from './resource-from-item' diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx index 1dbf07425e5..9ed7bc98ba1 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/organization-resource-inventory.tsx @@ -3,16 +3,22 @@ import { type AvailableResources, useAvailableResources, } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/available-resources' +import type { MothershipResourceType } from '@/app/workspace/[workspaceId]/home/types' /** Reuses each workspace's canonical inventory without introducing another picker. */ export function OrganizationResourceInventory({ workspaceId, + excludeTypes, onChange, }: { workspaceId: string + excludeTypes?: readonly MothershipResourceType[] onChange: (workspaceId: string, inventory: AvailableResources) => void }) { - const inventory = useAvailableResources(workspaceId, { includeFolderMentions: true }) + const inventory = useAvailableResources(workspaceId, { + includeFolderMentions: true, + excludeTypes, + }) useEffect(() => { onChange(workspaceId, inventory) }, [workspaceId, inventory, onChange]) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx index e6b70362da4..02027b3a060 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx @@ -58,6 +58,11 @@ import { useWorkspacePermissionsContext, } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' import { Table } from '@/app/workspace/[workspaceId]/tables/[tableId]/table' +import { + TestsFeatureGate, + TestView, + useTestRunAction, +} from '@/app/workspace/[workspaceId]/tests/components' import { useUsageLimits } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/hooks' import { useWorkflowExecution } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution' import { useFolders } from '@/hooks/queries/folders' @@ -66,6 +71,7 @@ import { exportTable } from '@/hooks/queries/tables' import { fetchWorkflowEnvelope } from '@/hooks/queries/utils/fetch-workflow-envelope' import { workflowKeys } from '@/hooks/queries/utils/workflow-keys' import { mapWorkflow } from '@/hooks/queries/utils/workflow-list-query' +import { useWorkflowTest } from '@/hooks/queries/workflow-tests' import { useWorkflows, WORKFLOW_STATE_STALE_TIME } from '@/hooks/queries/workflows' import { useAddressedWorkspaceFileRecord, useWorkspaceFiles } from '@/hooks/queries/workspace-files' import { createWorkspaceFileContentSource } from '@/hooks/use-file-content-source' @@ -306,6 +312,19 @@ export const ResourceContent = memo(function ResourceContent({ case 'dashboard': return + case 'test': + return ( + +
+ +
+
+ ) case 'file': return ( + case 'test': + return case 'table': return case 'log': @@ -554,6 +575,57 @@ function EmbeddedDashboardActions({ workspaceId }: EmbeddedDashboardActionsProps ) } +interface EmbeddedTestActionsProps { + workspaceId: string + name: string +} + +/** Runs the test the tab shows, and opens its page. */ +function EmbeddedTestActions({ workspaceId, name }: EmbeddedTestActionsProps) { + const router = useRouter() + const testsEnabled = useFeatureFlag('workflow-tests') + const detail = useWorkflowTest(workspaceId, name) + const run = useTestRunAction({ + workspaceId, + name, + running: detail.data?.latestRun?.status === 'running', + }) + if (!testsEnabled) return null + return ( + <> + + + + + + + +

{run.text}

+
+
+ + + router.push(`/workspace/${workspaceId}/tests/${name}`)} + aria-label='Open test' + > + + + + +

Open test

+
+
+ + ) +} + interface EmbeddedKnowledgeBaseActionsProps { workspaceId: string knowledgeBaseId: string diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-invalidation.ts b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-invalidation.ts index 6962cdab5f8..66fdac714ee 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-invalidation.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-invalidation.ts @@ -9,6 +9,7 @@ import { invalidateWorkflowLists } from '@/hooks/queries/utils/invalidate-workfl import { knowledgeKeys } from '@/hooks/queries/utils/knowledge-keys' import { tableKeys } from '@/hooks/queries/utils/table-keys' import { workflowKeys } from '@/hooks/queries/utils/workflow-keys' +import { workflowTestKeys } from '@/hooks/queries/workflow-tests' import { workspaceFileFolderKeys } from '@/hooks/queries/workspace-file-folders' import { workspaceFilesKeys } from '@/hooks/queries/workspace-files' @@ -28,6 +29,10 @@ const RESOURCE_INVALIDATORS: Record< invalidate(qc, id ? tableKeys.views(id) : tableKeys.viewsRoot()) }, dashboard: (qc, wId) => invalidate(qc, dashboardKeys.workspace(wId)), + test: (qc) => { + invalidate(qc, workflowTestKeys.lists()) + invalidate(qc, workflowTestKeys.details()) + }, file: (qc, wId, id) => { invalidate(qc, workspaceFilesKeys.lists()) invalidate(qc, id ? workspaceFilesKeys.record(wId, id) : workspaceFilesKeys.records()) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx index 1a9de189f0d..ae635b51111 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry/resource-registry.tsx @@ -11,6 +11,7 @@ import { Globe, Library, Search, + ShieldCheck, Table as TableIcon, Task, TerminalWindow, @@ -193,6 +194,15 @@ export const RESOURCE_REGISTRY: Record , }, + test: { + type: 'test', + label: 'Tests', + icon: ShieldCheck, + renderTabIcon: (_resource, className) => ( + + ), + renderDropdownItem: (props) => , + }, file: { type: 'file', label: 'Files', @@ -299,6 +309,7 @@ export const RESOURCE_MENU_ORDER: readonly MothershipResourceType[] = [ 'integration', 'task', 'dashboard', + 'test', 'table', 'file', 'filefolder', diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx index 9fb538d0330..dbcc1f2f202 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx @@ -203,7 +203,7 @@ export const MothershipView = memo( const activeFile = active?.type === 'file' ? files?.find((f) => f.id === active.id) : undefined const isActiveCsv = active?.type === 'file' && getFileExtension(active.title) === 'csv' - const isActivePreviewable = + const isActiveFilePreviewable = canEdit && active?.type === 'file' && // Dashboards store extensionless names, so the record's type decides once it loads. @@ -221,6 +221,8 @@ export const MothershipView = memo( // A Sim page is locked to its rendered view (the pdf model — the raw // source is not a mode this surface offers), so no toggle either. activeFile?.type !== SIM_PAGE_CONTENT_TYPE + // A test shows its source, its results, or both, like an HTML file; the viewer enforces edits. + const isActivePreviewable = isActiveFilePreviewable || active?.type === 'test' return ( ({ kind: 'dashboard', dashboardId: r.id, label: r.title }), + /** Sim reads a test through its file, `tests/.test.js`, not a chat context. */ + test: () => null, file: (r) => ({ kind: 'file', fileId: r.id, label: r.title }), folder: (r) => ({ kind: 'folder', folderId: r.id, label: r.title }), filefolder: (r) => ({ kind: 'filefolder', fileFolderId: r.id, label: r.title }), diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown/plus-menu-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown/plus-menu-dropdown.tsx index 3759f6e3d81..d7ddfa8ef0c 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown/plus-menu-dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/plus-menu-dropdown/plus-menu-dropdown.tsx @@ -15,6 +15,7 @@ import { import { IdentityTile } from '@/components/identity-tile/identity-tile' import { getWorkspaceInitial } from '@/lib/workspaces/initials' import { + COMPOSER_EXCLUDED_TYPES, ResourceMenuSections, resourceFromItem, useAvailableResources, @@ -154,6 +155,7 @@ export const PlusMenuDropdown = React.memo( const workspaceInventory = useAvailableResources(organizationId ? '' : workspaceId, { enabled: inventoryEnabled, includeFolderMentions: true, + excludeTypes: COMPOSER_EXCLUDED_TYPES, }) const { data: allWorkspaces, isPending: workspacesPending } = useOrderedWorkspacesQuery( Boolean(organizationId) && inventoryEnabled @@ -411,6 +413,7 @@ export const PlusMenuDropdown = React.memo( ))} diff --git a/apps/sim/app/workspace/[workspaceId]/home/hooks/stream/handle-tool-event.ts b/apps/sim/app/workspace/[workspaceId]/home/hooks/stream/handle-tool-event.ts index 9e8e50663b0..c62b2cb548a 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/hooks/stream/handle-tool-event.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/hooks/stream/handle-tool-event.ts @@ -1,5 +1,6 @@ import { isCurrentBrowserToolName } from '@sim/browser-protocol' import { isTerminalToolName } from '@sim/terminal-protocol' +import { toRecordOrNull } from '@sim/utils/object' import { isDesktopApp } from '@/lib/desktop' import { MothershipStreamV1ToolPhase, @@ -97,12 +98,12 @@ function runToolResultSideEffects(ctx: StreamLoopContext, node: ToolNode, replay invalidateResourceQueries(deps.queryClient, deps.workspaceId, resource.type, resource.id) } - if (!replay && (name === ApplyFileEdit.id || name === PrepareFileEdit.id) && isSuccess) { - const out = output as Record | undefined - const editData = - out && typeof out.data === 'object' && out.data !== null - ? (out.data as Record) - : undefined + const isFileEdit = name === ApplyFileEdit.id || name === PrepareFileEdit.id + const editData = isFileEdit ? toRecordOrNull(toRecordOrNull(output)?.data) : null + /** A file another resource owns (a test's source) never opens as a file tab. */ + const editedFileIsTab = editData?.fileTab !== false + + if (!replay && isFileEdit && isSuccess && editedFileIsTab) { const editedFileId = (typeof editData?.id === 'string' ? editData.id : undefined) ?? deps.previewSessionRef.current?.fileId @@ -139,7 +140,9 @@ function runToolResultSideEffects(ctx: StreamLoopContext, node: ToolNode, replay if (name === PrepareFileEdit.id) { deps.removePreviewSessionImmediate(node.id) } - const fileResource = extractedResources.find((r) => r.type === 'file') + const fileResource = editedFileIsTab + ? extractedResources.find((r) => r.type === 'file') + : undefined if (fileResource) { deps.promoteFileResource(fileResource.id, fileResource.title) deps.onResourceEventRef.current?.( diff --git a/apps/sim/app/workspace/[workspaceId]/layout.tsx b/apps/sim/app/workspace/[workspaceId]/layout.tsx index 6d324e690ee..5ee72f7ed95 100644 --- a/apps/sim/app/workspace/[workspaceId]/layout.tsx +++ b/apps/sim/app/workspace/[workspaceId]/layout.tsx @@ -11,6 +11,7 @@ import { } from '@/lib/desktop/executor/availability' import { isMothershipModelSelectorEnabled, isPlanModeEnabled } from '@/lib/mothership/feature-flags' import { resolveOrganizationEntryPath } from '@/lib/navigation/resolve-app-entry' +import { isWorkflowTestsEnabled } from '@/lib/workflow-tests/feature-flag' import { getQueryClient } from '@/app/_shell/providers/get-query-client' import { ImpersonationBanner } from '@/app/workspace/[workspaceId]/components/impersonation-banner' import { SessionExpired } from '@/app/workspace/[workspaceId]/components/session-expired' @@ -69,6 +70,7 @@ export default async function WorkspaceLayout({ planModeEnabled, organizationHref, dashboardsEnabled, + workflowTestsEnabled, desktopExecutorRegistered, ] = await Promise.all([ cookies(), @@ -86,6 +88,7 @@ export default async function WorkspaceLayout({ isPlanModeEnabled(), resolveOrganizationEntryPath(session), isDashboardsEnabled(hostContext.hostOrganizationId), + isWorkflowTestsEnabled(hostContext.hostOrganizationId), hasDesktopBackgroundExecutor(session.user.id), prefetchWorkspaceAccess(queryClient, workspaceId, principal), prefetchWorkspaceForkAvailability(queryClient, workspaceId, principal, hostContext), @@ -97,6 +100,7 @@ export default async function WorkspaceLayout({ +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/[name]/page.tsx b/apps/sim/app/workspace/[workspaceId]/tests/[name]/page.tsx new file mode 100644 index 00000000000..c00a9648606 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/[name]/page.tsx @@ -0,0 +1,13 @@ +import type { Metadata } from 'next' +import { TestDetail } from '@/app/workspace/[workspaceId]/tests/components' + +export const metadata: Metadata = { title: 'Test', robots: { index: false } } + +interface TestPageProps { + params: Promise<{ workspaceId: string; name: string }> +} + +export default async function TestPage({ params }: TestPageProps) { + const { workspaceId, name } = await params + return +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/index.ts b/apps/sim/app/workspace/[workspaceId]/tests/components/index.ts new file mode 100644 index 00000000000..e6e032c0ff0 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/index.ts @@ -0,0 +1,2 @@ +export { TestDetail, TestView, useTestRunAction } from './test-detail' +export { TestsFeatureGate } from './tests-feature-gate' diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx new file mode 100644 index 00000000000..673e7c50d73 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx @@ -0,0 +1,398 @@ +'use client' + +import { lazy, type ReactNode, Suspense, useEffect, useRef, useState } from 'react' +import { + Badge, + cn, + DashboardMetric, + scrollFadeAttributes, + scrollFadeClass, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + useScrollEdges, +} from '@sim/emcn' +import { Loader } from '@sim/emcn/icons' +import { formatDuration, formatRelativeTime } from '@sim/utils/formatting' +import type { WorkflowTestDetail } from '@/lib/api/contracts/workflow-tests' +import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section' +import { TestRunPicker } from '@/app/workspace/[workspaceId]/tests/components/test-run-picker' +import { useWorkflowTestRun } from '@/hooks/queries/workflow-tests' +import { testRunSelectionKey, useTestRunSelectionStore } from '@/stores/workflow-tests/store' + +const WorkflowVersionPreview = lazy(() => + import('@/app/workspace/[workspaceId]/tests/components/workflow-version-preview').then( + (module) => ({ default: module.WorkflowVersionPreview }) + ) +) + +const ExecutionSnapshot = lazy(() => + import( + '@/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot' + ).then((module) => ({ default: module.ExecutionSnapshot })) +) + +type RunDetail = NonNullable +type CaseResult = NonNullable['tests'][number] +type CaseState = 'queued' | 'running' | 'pass' | 'fail' | 'skip' | 'none' + +const CASE_STATE: Record< + CaseState, + { label: string; variant: 'green' | 'red' | 'amber' | 'gray' } +> = { + queued: { label: 'Queued', variant: 'gray' }, + running: { label: 'Running', variant: 'amber' }, + pass: { label: 'Passed', variant: 'green' }, + fail: { label: 'Failed', variant: 'red' }, + skip: { label: 'Skipped', variant: 'gray' }, + none: { label: 'Not run', variant: 'gray' }, +} + +interface TestDashboardProps { + workspaceId: string + name: string + detail: WorkflowTestDetail +} + +/** + * How the test is doing in one run (the latest unless the run picker chose another): totals, + * freshness, every case, and what it ran against. A latest run still going shows live. + */ +export function TestDashboard({ workspaceId, name, detail }: TestDashboardProps) { + const scrollRef = useRef(null) + const edges = useScrollEdges(scrollRef) + const selectedRunId = + useTestRunSelectionStore( + (state) => state.selectedRunIds[testRunSelectionKey(workspaceId, name)] + ) ?? null + const latestRunning = detail.latestRun?.status === 'running' + const viewedRunId = latestRunning ? null : selectedRunId + const selected = useWorkflowTestRun(workspaceId, name, viewedRunId) + const run = viewedRunId ? (selected.data ?? null) : detail.latestRun + const running = run?.status === 'running' + + const progress = run?.progress ?? {} + const results = new Map() + for (const result of run?.report?.tests ?? []) results.set(result.path.join(' > '), result) + /** A finished run lists the cases it ran; a running or missing one, the file's current cases. */ + const casePaths = + !running && run?.report + ? run.report.tests.map((result) => result.path) + : detail.test.cases.map((testCase) => testCase.path) + const cases = casePaths.map((path) => { + const key = path.join(' > ') + const result = results.get(key) + const state = caseState(running ? progress[key] : result?.status, running) + return { key, name: caseName(path), state, result } + }) + const finished = cases.filter((c) => c.state !== 'queued' && c.state !== 'running').length + const hasResults = running || Boolean(run?.report) + const count = (state: CaseState) => cases.filter((testCase) => testCase.state === state).length + const durationMs = run?.report?.tests.reduce((total, result) => total + result.durationMs, 0) + + return ( +
+
+
+
+ +
+
+ + + {running && run ? ( + + ) : ( + + )} +
+ {running && ( +
+
+
+ )} + +
+ + {cases.length === 0 ? ( +

No tests yet

+ ) : ( + + + + Test + Status + Duration + + + + {cases.map((testCase) => ( + + + {testCase.name} + {testCase.state === 'fail' && testCase.result && ( +

+ {failureReason(testCase.result)} +

+ )} +
+ + + {CASE_STATE[testCase.state].label} + + + + {testCase.result ? formatDuration(testCase.result.durationMs) : '—'} + +
+ ))} +
+
+ )} + + {!running && run && run.ranAgainst.length > 0 && ( + + )} +
+
+ ) +} + +interface FreshnessProps { + run: RunDetail | null + loading: boolean +} + +/** Says what is off about the run on screen; a finished, current run needs no line. */ +function Freshness({ run, loading }: FreshnessProps) { + const line = loading + ? { + lead: , + text: 'Loading run…', + } + : freshnessLine(run) + if (!line) return null + return ( +

+ {line.lead} + {line.text} +

+ ) +} + +function freshnessLine(run: RunDetail | null): { lead: ReactNode; text: string } | null { + if (!run) return { lead: null, text: 'Not run yet' } + if (run.status === 'running') { + return { + lead: ( + + Running + + ), + text: 'Against the latest workflows', + } + } + const when = formatRelativeTime(run.completedAt ?? run.startedAt) + if (run.status === 'error') { + return { + lead: ( + + Didn’t finish + + ), + text: `${when}: ${run.error ?? 'the run failed'}`, + } + } + if (!run.current) { + const changed = run.ranAgainst + .filter((workflow) => workflow.stale) + .map((workflow) => workflow.name ?? 'A deleted workflow') + return { + lead: ( + + Out of date + + ), + text: + changed.length > 0 + ? `${changed.join(', ')} changed after this run ${when}` + : `The test changed after this run ${when}`, + } + } + return null +} + +interface WorkflowsSectionProps { + workflows: RunDetail['ranAgainst'] +} + +type Previewing = + | { kind: 'version'; workflowId: string; version: number; title: string } + | { kind: 'snapshot'; executionId: string } + +/** + * Each workflow the run executed and the version it ran, marked when that version is no longer + * live. A deployed version opens on a read-only canvas; a draft opens as it was when the run used it. + */ +function WorkflowsSection({ workflows }: WorkflowsSectionProps) { + const [previewing, setPreviewing] = useState(null) + return ( + + + + {workflows.map((workflow) => { + const name = workflow.name ?? 'Deleted workflow' + const { executionId, version } = workflow + const preview = + workflow.name === null + ? undefined + : workflow.draft + ? executionId === null + ? undefined + : () => setPreviewing({ kind: 'snapshot', executionId }) + : version !== null + ? () => + setPreviewing({ + kind: 'version', + workflowId: workflow.workflowId, + version, + title: `${name} · v${version}`, + }) + : undefined + return ( + { + if (preview && (event.key === 'Enter' || event.key === ' ')) { + event.preventDefault() + preview() + } + }} + className={cn( + preview && 'cursor-pointer transition-colors hover:bg-[var(--surface-active)]' + )} + > + + + {name} + {workflow.draft ? ( + + Draft + + ) : ( + version !== null && ( + v{version} + ) + )} + + + + {workflow.stale && ( + + Out of date + + )} + + + ) + })} + +
+ {previewing?.kind === 'version' && ( + + setPreviewing(null)} + /> + + )} + {previewing?.kind === 'snapshot' && ( + + setPreviewing(null)} + /> + + )} +
+ ) +} + +/** A case with no status yet is queued while the run goes, and not run once it ended. */ +function caseState(status: CaseState | undefined, running: boolean): CaseState { + return status ?? (running ? 'queued' : 'none') +} + +/** A case's name under its file's one top-level describe. */ +function caseName(path: string[]): string { + return path.slice(1).join(' › ') +} + +/** Why a case failed, in the words a reader can act on: the judge's reason, else the message. */ +function failureReason(result: CaseResult): string { + const check = result.checks.find((candidate) => candidate.status === 'fail') + return check?.judge ?? check?.message ?? result.error?.message ?? 'Failed' +} + +interface ElapsedMetricProps { + startedAt: string +} + +/** The running clock: time since the run started, ticking while it goes. */ +function ElapsedMetric({ startedAt }: ElapsedMetricProps) { + const [now, setNow] = useState(() => Date.now()) + useEffect(() => { + const timer = setInterval(() => setNow(Date.now()), 250) + return () => clearInterval(timer) + }, []) + return ( + + ) +} + +interface MetricProps { + label: string + value: string | number +} + +function Metric({ label, value }: MetricProps) { + return ( +
+ +
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx new file mode 100644 index 00000000000..3ea390f13c5 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx @@ -0,0 +1,164 @@ +'use client' + +import { useEffect, useState } from 'react' +import { toast } from '@sim/emcn' +import { Columns2, Eye, Pencil, PlayOutline, ShieldCheck } from '@sim/emcn/icons' +import { getErrorMessage } from '@sim/utils/errors' +import { useRouter } from 'next/navigation' +import type { WorkflowTestDetail } from '@/lib/api/contracts/workflow-tests' +import { Resource } from '@/app/workspace/[workspaceId]/components/resource/resource' +import { + FileViewer, + type PreviewMode, +} from '@/app/workspace/[workspaceId]/files/components/file-viewer' +import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' +import { TestDashboard } from '@/app/workspace/[workspaceId]/tests/components/test-dashboard' +import { + useRefreshWorkflowTest, + useRunWorkflowTests, + useWorkflowTest, +} from '@/hooks/queries/workflow-tests' +import { useAddressedWorkspaceFileRecord } from '@/hooks/queries/workspace-files' +import { testRunSelectionKey, useTestRunSelectionStore } from '@/stores/workflow-tests/store' + +interface TestTarget { + workspaceId: string + name: string +} + +/** Tests run here against the deployed workflows; Sim runs drafts while it edits them. */ +const VERSION = 'deployed' as const + +const NEXT_PREVIEW_MODE: Record = { + editor: 'split', + split: 'preview', + preview: 'editor', +} + +/** The test's page: the shared view under a header that switches its mode and runs it. */ +export function TestDetail({ workspaceId, name }: TestTarget) { + const router = useRouter() + const query = useWorkflowTest(workspaceId, name) + const run = useTestRunAction({ workspaceId, name, running: isRunning(query.data) }) + const [previewMode, setPreviewMode] = useState('preview') + + return ( + + router.push(`/workspace/${workspaceId}/tests`), + }, + { label: query.data?.test.title ?? name }, + ]} + actions={[ + { + text: previewMode === 'editor' ? 'Split' : previewMode === 'split' ? 'Preview' : 'Edit', + icon: previewMode === 'editor' ? Columns2 : previewMode === 'split' ? Eye : Pencil, + onSelect: () => setPreviewMode((mode) => NEXT_PREVIEW_MODE[mode]), + }, + run, + ]} + /> + + + ) +} + +interface TestRunActionInput extends TestTarget { + running: boolean +} + +/** The Run action for one test, shared by the page header and the chat tab. */ +export function useTestRunAction({ workspaceId, name, running }: TestRunActionInput) { + const canEdit = useUserPermissionsContext().canEdit === true + const runTests = useRunWorkflowTests(workspaceId) + const unsaved = useTestRunSelectionStore( + (state) => state.unsavedTests[testRunSelectionKey(workspaceId, name)] === true + ) + const busy = running || runTests.isPending + return { + id: 'run', + icon: PlayOutline, + text: busy ? 'Running…' : unsaved ? 'Unsaved' : 'Run', + disabled: !canEdit || busy || unsaved, + onSelect: () => + runTests.mutate( + { version: VERSION, names: [name] }, + { onError: (error) => toast.error(error.message) } + ), + } +} + +function isRunning(detail: WorkflowTestDetail | undefined): boolean { + return detail?.latestRun?.status === 'running' +} + +interface TestViewProps extends TestTarget { + previewMode: PreviewMode +} + +/** + * A test file shown the way an HTML file is: its source, its results as the preview, or both + * side by side. The results are what the file declared at its last save. Shared by the test page + * and its resource tab in Chat. Every save is checked on the server; a refused save keeps the + * edit and says why above the editor. + */ +export function TestView({ workspaceId, name, previewMode }: TestViewProps) { + const canEdit = useUserPermissionsContext().canEdit === true + const query = useWorkflowTest(workspaceId, name) + const refreshTest = useRefreshWorkflowTest(workspaceId, name) + const [saveError, setSaveError] = useState(null) + const setUnsaved = useTestRunSelectionStore((state) => state.setUnsaved) + const testKey = testRunSelectionKey(workspaceId, name) + useEffect(() => () => setUnsaved(testKey, false), [setUnsaved, testKey]) + const detail = query.data + const file = useAddressedWorkspaceFileRecord(workspaceId, detail?.test.fileId ?? '', { + enabled: Boolean(detail), + }) + + const error = query.error ?? file.error + if (error) { + return ( +

+ {error.message} +

+ ) + } + if (!detail || !file.data) return null + + return ( +
+ {saveError && previewMode !== 'preview' && ( +
+ Not saved: + {saveError} +
+ )} + } + sourceSide='end' + enableFind + onDirtyChange={(dirty) => setUnsaved(testKey, dirty)} + onSaveError={(saveFailure) => + setSaveError(getErrorMessage(saveFailure, 'The test file was not saved')) + } + onSaveStatusChange={(status) => { + if (status !== 'saved') return + setSaveError(null) + refreshTest() + }} + /> +
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-run-picker.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-run-picker.tsx new file mode 100644 index 00000000000..e6829344275 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-run-picker.tsx @@ -0,0 +1,103 @@ +'use client' + +import { + Badge, + Chip, + ChipChevronDown, + cn, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuItemLabel, + DropdownMenuTrigger, +} from '@sim/emcn' +import { Check } from '@sim/emcn/icons' +import { formatRelativeTime } from '@sim/utils/formatting' +import type { WorkflowTestDetail } from '@/lib/api/contracts/workflow-tests' +import { testRunSelectionKey, useTestRunSelectionStore } from '@/stores/workflow-tests/store' + +type Run = WorkflowTestDetail['history'][number] + +interface TestRunPickerProps { + workspaceId: string + name: string + detail: WorkflowTestDetail +} + +/** Chooses which run the test's results show; the latest unless another is picked. */ +export function TestRunPicker({ workspaceId, name, detail }: TestRunPickerProps) { + const key = testRunSelectionKey(workspaceId, name) + const selectedRunId = useTestRunSelectionStore((state) => state.selectedRunIds[key]) + const selectRun = useTestRunSelectionStore((state) => state.selectRun) + const runs = detail.history + const latestId = detail.latestRun?.id + const shownId = detail.latestRun?.status === 'running' ? latestId : (selectedRunId ?? latestId) + const shown = runs.find((run) => run.id === shownId) + if (!shown) return null + + return ( + + + } rightAdornment={}> + {runLabel(shown, shown.id === latestId)} + + + + {runs.map((run) => ( + selectRun(key, run.id === latestId ? null : run.id)} + > + + + + {runResult(run)} + {run.version === 'draft' && ( + + Draft + + )} + {!run.current && ( + + Out of date + + )} + + {run.id === shownId && ( + + )} + + ))} + + + ) +} + +function runLabel(run: Run, latest: boolean): string { + const when = formatRelativeTime(run.completedAt ?? run.startedAt) + return latest ? `Latest · ${when}` : when +} + +function runResult(run: Run): string { + if (run.status === 'passed' || run.status === 'failed') { + return `${run.passed}/${run.passed + run.failed}` + } + return run.status === 'error' ? 'Didn’t finish' : 'Running' +} + +const RUN_DOT: Record = { + passed: 'bg-[var(--badge-success-text)]', + running: 'bg-[var(--badge-amber-text)]', + failed: 'bg-[var(--badge-error-text)]', + error: 'bg-[var(--badge-error-text)]', +} + +interface RunDotProps { + run: Run +} + +function RunDot({ run }: RunDotProps) { + return ( + + ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/test-status-badge.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/test-status-badge.tsx new file mode 100644 index 00000000000..cb25f17d688 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/test-status-badge.tsx @@ -0,0 +1,28 @@ +import { Badge } from '@sim/emcn' +import type { WorkflowTestRecord } from '@/lib/api/contracts/workflow-tests' + +type BadgeVariant = 'green' | 'red' | 'amber' | 'gray' +type TestStatus = WorkflowTestRecord['status'] + +const TEST_STATUS: Record = { + passing: { label: 'Passed', variant: 'green' }, + failing: { label: 'Failed', variant: 'red' }, + error: { label: 'Didn’t run', variant: 'red' }, + running: { label: 'Running', variant: 'amber' }, + changed: { label: 'Changed', variant: 'gray' }, + not_run: { label: 'Not run', variant: 'gray' }, +} + +interface TestStatusBadgeProps { + status: TestStatus +} + +/** A test's latest outcome, styled like a log's status. */ +export function TestStatusBadge({ status }: TestStatusBadgeProps) { + const config = TEST_STATUS[status] + return ( + + {config.label} + + ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/tests-feature-gate.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/tests-feature-gate.tsx new file mode 100644 index 00000000000..f83eafd4761 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/tests-feature-gate.tsx @@ -0,0 +1,19 @@ +'use client' + +import type { ReactNode } from 'react' +import { useFeatureFlag } from '@/app/workspace/[workspaceId]/providers/feature-flags-provider' + +interface TestsFeatureGateProps { + children: ReactNode +} + +export function TestsFeatureGate({ children }: TestsFeatureGateProps) { + const enabled = useFeatureFlag('workflow-tests') + return enabled ? ( + children + ) : ( +
+ Tests are not enabled +
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/components/workflow-version-preview.tsx b/apps/sim/app/workspace/[workspaceId]/tests/components/workflow-version-preview.tsx new file mode 100644 index 00000000000..733be87472f --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/components/workflow-version-preview.tsx @@ -0,0 +1,50 @@ +'use client' + +import { useId } from 'react' +import { ChipModal, ChipModalBody, ChipModalHeader } from '@sim/emcn' +import { Preview } from '@/app/workspace/[workspaceId]/w/components/preview' +import { useDeploymentVersionState } from '@/hooks/queries/workflows' + +interface WorkflowVersionPreviewProps { + workflowId: string + version: number + title: string + onClose: () => void +} + +/** A deployed workflow version on a read-only canvas, as the deploy modal shows it. */ +export function WorkflowVersionPreview({ + workflowId, + version, + title, + onClose, +}: WorkflowVersionPreviewProps) { + const descriptionId = useId() + const { data: workflowState, error } = useDeploymentVersionState(workflowId, version) + return ( + { + if (!open) onClose() + }} + srTitle='Workflow preview' + aria-describedby={descriptionId} + size='full' + className='h-[90vh] [&>div]:h-full' + > + {title} + +

+ Visual preview of the workflow version this test ran against. +

+ {error ? ( +

+ {error.message} +

+ ) : workflowState ? ( + + ) : null} +
+
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/layout.tsx b/apps/sim/app/workspace/[workspaceId]/tests/layout.tsx new file mode 100644 index 00000000000..fafe1fdf8a9 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/layout.tsx @@ -0,0 +1,10 @@ +import type { ReactNode } from 'react' +import { TestsFeatureGate } from '@/app/workspace/[workspaceId]/tests/components' + +interface TestsLayoutProps { + children: ReactNode +} + +export default function TestsLayout({ children }: TestsLayoutProps) { + return {children} +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/loading.tsx b/apps/sim/app/workspace/[workspaceId]/tests/loading.tsx new file mode 100644 index 00000000000..23cb60115e6 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/loading.tsx @@ -0,0 +1,8 @@ +'use client' + +import { ShieldCheck } from '@sim/emcn/icons' +import { ResourceChromeFallback } from '@/app/workspace/[workspaceId]/components/resource/components/resource-chrome-fallback' + +export default function TestsLoading() { + return +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/page.tsx b/apps/sim/app/workspace/[workspaceId]/tests/page.tsx new file mode 100644 index 00000000000..671433413e8 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/page.tsx @@ -0,0 +1,19 @@ +import { Suspense } from 'react' +import type { Metadata } from 'next' +import TestsLoading from '@/app/workspace/[workspaceId]/tests/loading' +import { Tests } from '@/app/workspace/[workspaceId]/tests/tests' + +export const metadata: Metadata = { title: 'Tests', robots: { index: false } } + +interface TestsPageProps { + params: Promise<{ workspaceId: string }> +} + +export default async function TestsPage({ params }: TestsPageProps) { + const { workspaceId } = await params + return ( + }> + + + ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/tests/search-params.ts b/apps/sim/app/workspace/[workspaceId]/tests/search-params.ts new file mode 100644 index 00000000000..1d0a4236440 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/search-params.ts @@ -0,0 +1,18 @@ +import { parseAsString } from 'nuqs/server' +import { createSortParams } from '@/lib/url-state' + +/** Sortable columns, matching the `Resource.Options` sort menu. */ +const TEST_SORT_COLUMNS = ['name', 'cases', 'lastRun', 'updated'] as const + +/** Shared `sort` + `dir` params for the Tests list. Default: most recently updated first. */ +export const testsSortParams = createSortParams(TEST_SORT_COLUMNS, { + column: 'updated', + direction: 'desc', +}) + +/** `search` filters by title; the input is controlled by it and only its URL write is debounced. */ +export const testsParsers = { + search: parseAsString.withDefault(''), +} as const + +export const testsUrlKeys = { history: 'replace', clearOnDefault: true } as const diff --git a/apps/sim/app/workspace/[workspaceId]/tests/tests.tsx b/apps/sim/app/workspace/[workspaceId]/tests/tests.tsx new file mode 100644 index 00000000000..4960ec0ba57 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/tests/tests.tsx @@ -0,0 +1,168 @@ +'use client' + +import { useMemo } from 'react' +import { Chip, toast } from '@sim/emcn' +import { PlayOutline, ShieldCheck } from '@sim/emcn/icons' +import { useRouter } from 'next/navigation' +import { useQueryStates } from 'nuqs' +import type { WorkflowTestRecord } from '@/lib/api/contracts/workflow-tests' +import { SEARCH_DEBOUNCE_MS } from '@/lib/url-state' +import { ownerCell } from '@/app/workspace/[workspaceId]/components/resource/components/owner-cell' +import { ResourceNoResults } from '@/app/workspace/[workspaceId]/components/resource/components/resource-empty-state' +import type { + SearchConfig, + SortConfig, +} from '@/app/workspace/[workspaceId]/components/resource/components/resource-options' +import { timeCell } from '@/app/workspace/[workspaceId]/components/resource/components/time-cell' +import { + Resource, + type ResourceColumn, + type ResourceRow, +} from '@/app/workspace/[workspaceId]/components/resource/resource' +import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' +import { TestStatusBadge } from '@/app/workspace/[workspaceId]/tests/components/test-status-badge' +import { + testsParsers, + testsSortParams, + testsUrlKeys, +} from '@/app/workspace/[workspaceId]/tests/search-params' +import { useRunWorkflowTests, useWorkflowTests } from '@/hooks/queries/workflow-tests' +import { useWorkspaceMembersQuery } from '@/hooks/queries/workspace' +import { useDebouncedSearchSetter } from '@/hooks/use-debounced-search-setter' +import { useSearchFilterValue } from '@/hooks/use-search-filter-value' +import { useUrlSort } from '@/hooks/use-url-sort' + +const COLUMNS: ResourceColumn[] = [ + { id: 'name', header: 'Name' }, + { id: 'status', header: 'Status' }, + { id: 'cases', header: 'Cases' }, + { id: 'owner', header: 'Created by' }, + { id: 'lastRun', header: 'Last run' }, + { id: 'updated', header: 'Last updated' }, +] + +const SORT_OPTIONS = [ + { id: 'name', label: 'Name' }, + { id: 'cases', label: 'Cases' }, + { id: 'lastRun', label: 'Last run' }, + { id: 'updated', label: 'Last updated' }, +] + +function lastRunAt(test: WorkflowTestRecord): string | null { + const latest = test.recentRuns[0] + return latest ? (latest.completedAt ?? latest.startedAt) : null +} + +const SORT_KEYS: Record< + (typeof SORT_OPTIONS)[number]['id'], + (test: WorkflowTestRecord) => string | number +> = { + name: (test) => test.title.toLowerCase(), + cases: (test) => test.caseCount, + lastRun: (test) => lastRunAt(test) ?? '', + updated: (test) => test.updatedAt, +} + +interface TestsProps { + workspaceId: string +} + +export function Tests({ workspaceId }: TestsProps) { + const router = useRouter() + const canEdit = useUserPermissionsContext().canEdit === true + const [{ search }, setFilters] = useQueryStates(testsParsers, testsUrlKeys) + const setSearch = useDebouncedSearchSetter((value, options) => + setFilters({ search: value }, options) + ) + const debouncedSearch = useSearchFilterValue(search, SEARCH_DEBOUNCE_MS) + const { sort, dir, activeSort, onSort, onClear } = useUrlSort(testsSortParams, testsUrlKeys) + const query = useWorkflowTests(workspaceId) + const { data: members } = useWorkspaceMembersQuery(workspaceId) + const runTests = useRunWorkflowTests(workspaceId) + + const membersById = useMemo( + () => new Map((members ?? []).map((member) => [member.userId, member])), + [members] + ) + + const tests = useMemo(() => { + const term = debouncedSearch.trim().toLowerCase() + const matching = (query.data ?? []).filter( + (test) => !term || test.title.toLowerCase().includes(term) || test.name.includes(term) + ) + const key = SORT_KEYS[sort] + const direction = dir === 'asc' ? 1 : -1 + return [...matching].sort((a, b) => { + const left = key(a) + const right = key(b) + return left < right ? -direction : left > right ? direction : 0 + }) + }, [query.data, debouncedSearch, sort, dir]) + + const rows: ResourceRow[] = useMemo( + () => + tests.map((test) => ({ + id: test.name, + cells: { + name: { icon: , label: test.title }, + status: { content: }, + cases: { label: String(test.caseCount) }, + owner: ownerCell(test.createdByUserId, membersById), + lastRun: timeCell(lastRunAt(test)), + updated: timeCell(test.updatedAt), + }, + })), + [tests, membersById] + ) + + const searchConfig: SearchConfig = { + value: search, + onChange: setSearch, + onClearAll: () => setSearch(''), + placeholder: 'Search tests...', + } + const sortConfig: SortConfig = { options: SORT_OPTIONS, active: activeSort, onSort, onClear } + const running = runTests.isPending || (query.data ?? []).some((test) => test.status === 'running') + + return ( + + + runTests.mutate( + { version: 'deployed' }, + { onError: (error) => toast.error(error.message) } + ), + }, + ]} + /> + + router.push(`/workspace/${workspaceId}/tests/${name}`)} + emptyState={ + query.error ? ( +
+

{query.error.message}

+ query.refetch()}>Retry +
+ ) : debouncedSearch.trim() && query.data?.length ? ( + setSearch('')} + /> + ) : undefined + } + /> +
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx index 44f17083375..31687797b85 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -34,6 +34,7 @@ import { Pin, Plus, Search, + ShieldCheck, Table, Task, Workflow, @@ -753,6 +754,7 @@ export const Sidebar = memo(function Sidebar({ organizationHref, desktopExecutor ) const dashboardsEnabled = useFeatureFlag('dashboards') + const testsEnabled = useFeatureFlag('workflow-tests') const topNavItems = useMemo( () => [ @@ -828,6 +830,14 @@ export const Sidebar = memo(function Sidebar({ organizationHref, desktopExecutor hidden: permissionConfig.hideKnowledgeBaseTab && !accessRequestsEnabled, restricted: permissionConfig.hideKnowledgeBaseTab, }, + { + id: 'tests', + label: 'Tests', + icon: ShieldCheck, + href: `/workspace/${workspaceId}/tests`, + hidden: !testsEnabled || (permissionConfig.hideFilesTab && !accessRequestsEnabled), + restricted: permissionConfig.hideFilesTab, + }, { id: 'logs', label: 'Logs', @@ -842,6 +852,7 @@ export const Sidebar = memo(function Sidebar({ organizationHref, desktopExecutor permissionConfig.hideKnowledgeBaseTab, permissionConfig.hideTablesTab, accessRequestsEnabled, + testsEnabled, ] ) diff --git a/apps/sim/executor/execution/block-executor.ts b/apps/sim/executor/execution/block-executor.ts index 0623ffa828f..4b1a84cd33b 100644 --- a/apps/sim/executor/execution/block-executor.ts +++ b/apps/sim/executor/execution/block-executor.ts @@ -35,6 +35,7 @@ import { isRetryableBlockError, resolveBlockRetryPolicy } from '@/executor/execu import type { BlockStateWriter, ContextExtensions, + ExecutionTestHooks, WorkflowNodeMetadata, } from '@/executor/execution/types' import { @@ -272,6 +273,9 @@ export class BlockExecutor { * for the same reason `streamingPartialOutput` above is. */ let completedHandlerCost: TrustedExecutionCost | undefined + const testHooks = this.contextExtensions.testHooks + const mockedBlockId = node.metadata.originalBlockId ?? block.id + const isMocked = !isSentinel && testHooks?.mocksBlock(mockedBlockId) === true try { /** * Only the handler call is retried. A streaming handler returns before any @@ -279,6 +283,17 @@ export class BlockExecutor { * already seen. */ const output = await this.runHandlerWithRetry(blockCtx, block, blockLog, (retry) => { + if (isMocked && testHooks) { + return this.resolveMockedBlock( + blockCtx, + node, + block, + mockedBlockId, + testHooks, + inputsForLog, + inputDisplayRegistry + ) + } const invocationMetadata = retry ? { ...nodeMetadata, retry } : nodeMetadata return handler.executeWithNode ? handler.executeWithNode(blockCtx, block, resolvedInputs, invocationMetadata) @@ -433,6 +448,13 @@ export class BlockExecutor { output: displayOutput, }) this.setBlockLogDisplayProvenance(blockLog, displayProvenance) + if (!isMocked && testHooks?.spiesBlock(mockedBlockId)) { + testHooks.recordSpy({ + blockId: mockedBlockId, + input: displayInput, + output: displayOutput, + }) + } this.fireBlockCompleteCallback( blockStartPromise, blockCtx, @@ -929,6 +951,35 @@ export class BlockExecutor { return { result: output } } + /** A test mock sees exactly what the block's execution log shows, never raw resolved secrets. */ + private resolveMockedBlock( + ctx: ExecutionContext, + node: DAGNode, + block: SerializedBlock, + blockId: string, + testHooks: ExecutionTestHooks, + inputs: Record, + registry: ResolvedSecretTraceRegistry | undefined + ): Promise { + const blockName = block.metadata?.name ?? blockId + const projection = registry?.projectResolvedInputSelection(inputs) + if (projection && !projection.complete) { + throw new Error(`Mocked block "${blockName}" has inputs whose secrets cannot be redacted`) + } + return testHooks.resolveMock( + { + blockId, + blockName, + blockType: block.metadata?.id ?? '', + input: this.sanitizeInputsForLog(projection?.value ?? inputs, block), + ...(node.metadata.branchIndex !== undefined + ? { branchIndex: node.metadata.branchIndex } + : {}), + }, + ctx.abortSignal + ) + } + /** Builds the log-facing input copy from resolver-recorded projections only. */ private projectInputsForDisplay( inputs: Record, diff --git a/apps/sim/executor/execution/executor.ts b/apps/sim/executor/execution/executor.ts index 1e049bab378..27c1b2f1822 100644 --- a/apps/sim/executor/execution/executor.ts +++ b/apps/sim/executor/execution/executor.ts @@ -87,6 +87,16 @@ export class DAGExecutor { } async execute(workflowId: string, triggerBlockId?: string): Promise { + await this.contextExtensions.testHooks?.enterWorkflow({ + workflowId, + blocks: this.workflow.blocks.map((block) => ({ + id: block.id, + name: block.metadata?.name ?? block.id, + type: block.metadata?.id ?? '', + params: block.config.params ?? {}, + })), + resolvedSecretTraceRegistry: this.contextExtensions.resolvedSecretTraceRegistry, + }) const savedIncomingEdges = this.contextExtensions.dagIncomingEdges const dag = this.dagBuilder.build(this.workflow, { triggerBlockId, @@ -444,6 +454,7 @@ export class DAGExecutor { isDeployedContext: this.contextExtensions.isDeployedContext, enforceCredentialAccess: this.contextExtensions.enforceCredentialAccess, piiBlockOutputRedaction: this.contextExtensions.piiBlockOutputRedaction, + testHooks: this.contextExtensions.testHooks, blockStates: state.getBlockStates(), blockLogs: restoredBlockLogs, /* diff --git a/apps/sim/executor/execution/test-hooks.test.ts b/apps/sim/executor/execution/test-hooks.test.ts new file mode 100644 index 00000000000..97dbdba0439 --- /dev/null +++ b/apps/sim/executor/execution/test-hooks.test.ts @@ -0,0 +1,229 @@ +import type { SessionPrincipal } from '@sim/auth/principal' +import { createSerializedBlock, createSerializedWorkflow } from '@sim/testing' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { + createMockChannel, + type MockChannel, + type MockMatcher, +} from '@/lib/workflow-tests/mock-channel' +import { BlockType } from '@/executor/constants' +import { DAGExecutor } from '@/executor/execution/executor' +import type { ExecutionResult } from '@/executor/types' +import { stripCloneSuffixes } from '@/executor/utils/subflow-node-id-codec' +import type { SerializedWorkflow } from '@/serializer/types' + +const { executed, inputsSeen } = vi.hoisted(() => ({ + executed: [] as string[], + inputsSeen: [] as Array<{ blockId: string; inputs: Record }>, +})) + +vi.mock('@/executor/handlers/registry', () => ({ + createBlockHandlers: () => [ + { + canHandle: () => true, + execute: async (_ctx: unknown, block: { id: string }, inputs: Record) => { + executed.push(block.id) + inputsSeen.push({ blockId: block.id, inputs }) + return { ok: true, from: block.id } + }, + }, + ], +})) + +const PRINCIPAL: SessionPrincipal = { kind: 'session', userId: 'user-1', sessionId: 'session-1' } + +const BLOCK_TYPES: Record = { + start: BlockType.STARTER, + loop: BlockType.LOOP, + fanOut: BlockType.PARALLEL, +} + +function workflow( + connections: SerializedWorkflow['connections'], + params: Record> = {}, + loops: SerializedWorkflow['loops'] = {}, + parallels: SerializedWorkflow['parallels'] = {} +): SerializedWorkflow { + const ids = new Set(connections.flatMap((c) => [c.source, c.target])) + const blocks = [...ids].map((id) => + createSerializedBlock({ + id, + name: id, + type: BLOCK_TYPES[id] ?? BlockType.API, + params: params[id] ?? {}, + }) + ) + return { ...createSerializedWorkflow(blocks, connections), loops, parallels } +} + +/** A test's matcher that mocks exactly these block ids and nothing else. */ +function mockingBlocks(blockIds: string[]): MockMatcher { + const mocked = new Set(blockIds) + return { + enterWorkflow: async () => {}, + mocksBlock: (blockId) => mocked.has(blockId), + mocksTool: () => false, + spiesBlock: () => false, + recordSpy: () => {}, + } +} + +function run( + wf: SerializedWorkflow, + channel: MockChannel, + abortSignal?: AbortSignal +): Promise { + return new DAGExecutor({ + workflow: wf, + contextExtensions: { + workspaceId: 'ws', + executionId: 'exec-1', + principal: PRINCIPAL, + testHooks: channel.hooks, + ...(abortSignal ? { abortSignal } : {}), + }, + }).execute('wf') +} + +const inputsOf = (blockId: string) => + inputsSeen.filter((seen) => stripCloneSuffixes(seen.blockId) === blockId).map((s) => s.inputs) + +describe('executor test hooks', () => { + beforeEach(() => { + executed.length = 0 + inputsSeen.length = 0 + }) + + const linear = workflow( + [ + { source: 'start', target: 'prep' }, + { source: 'prep', target: 'lookup' }, + { source: 'lookup', target: 'notify' }, + ], + { + lookup: { query: '' }, + notify: { plan: '' }, + } + ) + + it('parks a mocked block with its resolved inputs and feeds the reply downstream', async () => { + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(linear, channel) + + const parked = await channel.nextCall() + expect(parked.call.blockId).toBe('lookup') + expect(parked.call.blockName).toBe('lookup') + expect(parked.call.input).toEqual({ query: 'prep' }) + + channel.reply(parked.callId, { output: { plan: 'pro' } }) + const result = await done + + expect(result.success).toBe(true) + expect(executed).not.toContain('lookup') + expect(inputsOf('notify')).toEqual([{ plan: 'pro' }]) + }) + + it('fails the block, and the run, with the error a mock rejects with', async () => { + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(linear, channel) + + const parked = await channel.nextCall() + channel.reply(parked.callId, { error: 'rate limited (429)' }) + + await expect(done).rejects.toThrow('rate limited (429)') + expect(executed).not.toContain('notify') + }) + + it('stops a run that is cancelled while a mock is parked, and refuses a late reply', async () => { + const channel = createMockChannel(mockingBlocks(['lookup'])) + const abort = new AbortController() + const done = run(linear, channel, abort.signal) + + const parked = await channel.nextCall() + abort.abort() + const result = await done + + expect(result.success).toBe(false) + expect(executed).not.toContain('notify') + expect(() => channel.reply(parked.callId, { output: { plan: 'pro' } })).toThrow() + }) + + it('routes each parallel branch its own reply, whatever order the replies arrive in', async () => { + const parallel = workflow( + [ + { source: 'start', target: 'fanOut' }, + { source: 'fanOut', target: 'lookup', sourceHandle: 'parallel-start-source' }, + { source: 'lookup', target: 'notify' }, + ], + { notify: { plan: '' } }, + {}, + { + fanOut: { + id: 'fanOut', + nodes: ['lookup', 'notify'], + count: 3, + parallelType: 'count', + }, + } + ) + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(parallel, channel) + + const parked = [await channel.nextCall(), await channel.nextCall(), await channel.nextCall()] + for (const [i, call] of [...parked].reverse().entries()) { + channel.reply(call.callId, { output: { plan: `plan-${call.call.branchIndex}-${i}` } }) + } + const result = await done + + expect(result.success).toBe(true) + expect(parked.map((p) => p.call.branchIndex).sort()).toEqual([0, 1, 2]) + const plans = inputsOf('notify').map((inputs) => String(inputs.plan)) + expect(plans).toHaveLength(3) + expect(plans.map((plan) => plan.split('-')[1]).sort()).toEqual(['0', '1', '2']) + }) + + it('delivers one call per loop iteration, in order', async () => { + const looped = workflow( + [ + { source: 'start', target: 'loop' }, + { source: 'loop', target: 'lookup', sourceHandle: 'loop-start-source' }, + { source: 'lookup', target: 'notify' }, + ], + { notify: { plan: '' } }, + { loop: { id: 'loop', nodes: ['lookup', 'notify'], iterations: 2, loopType: 'for' } } + ) + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(looped, channel) + + for (const plan of ['first', 'second']) { + const parked = await channel.nextCall() + channel.reply(parked.callId, { output: { plan } }) + } + const result = await done + + expect(result.success).toBe(true) + expect(inputsOf('notify')).toEqual([{ plan: 'first' }, { plan: 'second' }]) + }) + + it('refuses a reply to a call that is unknown or already answered', async () => { + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(linear, channel) + + const parked = await channel.nextCall() + expect(() => channel.reply('nope', { output: {} })).toThrow() + channel.reply(parked.callId, { output: { plan: 'pro' } }) + expect(() => channel.reply(parked.callId, { output: { plan: 'pro' } })).toThrow() + await done + }) + + it('rejects every parked call when the channel closes, so the run cannot hang', async () => { + const channel = createMockChannel(mockingBlocks(['lookup'])) + const done = run(linear, channel) + + await channel.nextCall() + channel.close(new Error('test sandbox exited')) + + await expect(done).rejects.toThrow('test sandbox exited') + await expect(channel.nextCall()).rejects.toThrow('test sandbox exited') + }) +}) diff --git a/apps/sim/executor/execution/types.ts b/apps/sim/executor/execution/types.ts index 644b4337a39..a8cfcdb56f8 100644 --- a/apps/sim/executor/execution/types.ts +++ b/apps/sim/executor/execution/types.ts @@ -255,6 +255,59 @@ export interface PiiBlockOutputRedaction { customPatterns?: CustomPiiPattern[] } +/** A mocked block reached by a test run, carrying the inputs its execution log shows. */ +export interface MockedBlockCall { + blockId: string + blockName: string + blockType: string + input: Record + /** Set inside a parallel: which branch made the call. */ + branchIndex?: number +} + +/** A tool call an Agent block made that the test answers instead of the tool. */ +export interface MockedToolCall { + /** The calling Agent's workflow block id (never a parallel-branch clone id). */ + blockId: string + toolId: string + /** The arguments the model sent, with credentials and internal context removed. */ + input: Record +} + +/** One block of a workflow a test run entered, as the executor will run it. */ +export interface TestWorkflowBlock { + id: string + name: string + type: string + params: Record +} + +/** + * Hooks a workflow test run installs. Every workflow the run executes, the tested one and each + * child, announces its blocks with `enterWorkflow` before running; the test matches its mocks + * and spies against those blocks by name. A mocked block awaits `resolveMock` in place of its + * handler; everything after the handler (normalization, redaction, logging, edges) runs as + * usual. A mocked tool call awaits `resolveToolMock` in place of the tool. Child workflow + * executions inherit the hooks. + */ +export interface ExecutionTestHooks { + /** `resolvedSecretTraceRegistry` redacts what this run sends back to the test. */ + enterWorkflow(workflow: { + workflowId: string + blocks: TestWorkflowBlock[] + resolvedSecretTraceRegistry: ResolvedSecretTraceRegistry | undefined + }): Promise + /** Whether this block's handler is replaced; ids are workflow block ids, never clone ids. */ + mocksBlock(blockId: string): boolean + resolveMock(call: MockedBlockCall, abortSignal?: AbortSignal): Promise + /** Whether this Agent block's calls to this tool are answered by the test. */ + mocksTool(blockId: string, toolId: string): boolean + resolveToolMock(call: MockedToolCall, abortSignal?: AbortSignal): Promise> + /** Whether the test records this block's inputs and output while it runs for real. */ + spiesBlock(blockId: string): boolean + recordSpy(call: { blockId: string; input: unknown; output: unknown }): void +} + export interface ContextExtensions { workspaceId?: string executionId?: string @@ -352,6 +405,8 @@ export interface ContextExtensions { */ stopAfterBlockId?: string + testHooks?: ExecutionTestHooks + /** * Ordered list of workflow IDs in the current call chain, used for cycle detection. * Each hop appends the current workflow ID before making outgoing requests. diff --git a/apps/sim/executor/handlers/workflow/workflow-handler.ts b/apps/sim/executor/handlers/workflow/workflow-handler.ts index 2138d89bb63..321ecab58b7 100644 --- a/apps/sim/executor/handlers/workflow/workflow-handler.ts +++ b/apps/sim/executor/handlers/workflow/workflow-handler.ts @@ -888,6 +888,9 @@ export class WorkflowBlockHandler implements BlockHandler { // Propagate in-flight block-output redaction into child workflows so // nested blocks mask outputs too (recurses: each child forwards it). piiBlockOutputRedaction: ctx.piiBlockOutputRedaction, + // A workflow test's mocks and spies reach every child it runs, except a custom + // block's: that workflow is the publisher's, so the test mocks the block whole. + testHooks: isCustomBlock ? undefined : ctx.testHooks, callChain: childCallChain, // A custom block's own session markers and the parent's live stream, fanned // out together — see `childCallbacks` above for why this is not two spreads. diff --git a/apps/sim/executor/types.ts b/apps/sim/executor/types.ts index c09a2a11394..136536a769a 100644 --- a/apps/sim/executor/types.ts +++ b/apps/sim/executor/types.ts @@ -6,6 +6,7 @@ import type { BlockOutput } from '@/blocks/types' import type { ChildWorkflowContext, ExecutionCallbacks, + ExecutionTestHooks, IterationContext, ParentIteration, PiiBlockOutputRedaction, @@ -420,6 +421,8 @@ export interface ExecutorDelegationOrigin { export interface ExecutionContext { workflowId: string + /** Set during a workflow test run; Agent tool calls consult it before calling the tool. */ + testHooks?: ExecutionTestHooks workspaceId?: string executionId?: string largeValueExecutionIds?: string[] diff --git a/apps/sim/hooks/queries/workflow-tests.ts b/apps/sim/hooks/queries/workflow-tests.ts new file mode 100644 index 00000000000..a48a826c0f5 --- /dev/null +++ b/apps/sim/hooks/queries/workflow-tests.ts @@ -0,0 +1,105 @@ +import { useCallback } from 'react' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { requestJson } from '@/lib/api/client/request' +import { + getWorkflowTestContract, + getWorkflowTestRunContract, + listWorkflowTestsContract, + runWorkflowTestsContract, +} from '@/lib/api/contracts/workflow-tests' + +export type WorkflowTestVersion = 'draft' | 'deployed' + +const WORKFLOW_TEST_STALE_TIME = 15_000 +/** While a run is in flight, so its progress and result appear as they land. */ +const WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL = 3_000 +/** Otherwise, so a run started elsewhere (Sim, another person, the API) shows up. */ +const WORKFLOW_TEST_IDLE_REFETCH_INTERVAL = 15_000 + +export const workflowTestKeys = { + all: ['workflow-tests'] as const, + lists: () => [...workflowTestKeys.all, 'list'] as const, + list: (workspaceId: string) => [...workflowTestKeys.lists(), workspaceId] as const, + details: () => [...workflowTestKeys.all, 'detail'] as const, + test: (workspaceId: string, name: string) => + [...workflowTestKeys.details(), workspaceId, name] as const, + detail: (workspaceId: string, name: string) => + [...workflowTestKeys.test(workspaceId, name), 'detail'] as const, + run: (workspaceId: string, name: string, runId: string) => + [...workflowTestKeys.test(workspaceId, name), 'run', runId] as const, +} + +/** Every test with its recent runs, draft and deployed alike. */ +export function useWorkflowTests(workspaceId: string, options?: { enabled?: boolean }) { + return useQuery({ + queryKey: workflowTestKeys.list(workspaceId), + queryFn: ({ signal }) => + requestJson(listWorkflowTestsContract, { + params: { id: workspaceId }, + query: {}, + signal, + }), + enabled: Boolean(workspaceId) && (options?.enabled ?? true), + staleTime: WORKFLOW_TEST_STALE_TIME, + select: (data) => data.tests, + refetchInterval: (query) => + query.state.data?.tests.some((test) => test.status === 'running') + ? WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL + : WORKFLOW_TEST_IDLE_REFETCH_INTERVAL, + }) +} + +/** One test with its latest run and history, draft and deployed alike. */ +export function useWorkflowTest(workspaceId: string, name: string) { + return useQuery({ + queryKey: workflowTestKeys.detail(workspaceId, name), + queryFn: ({ signal }) => + requestJson(getWorkflowTestContract, { + params: { id: workspaceId, name }, + query: {}, + signal, + }), + enabled: Boolean(workspaceId && name), + staleTime: WORKFLOW_TEST_STALE_TIME, + refetchInterval: (query) => + query.state.data?.latestRun?.status === 'running' + ? WORKFLOW_TEST_RUNNING_REFETCH_INTERVAL + : WORKFLOW_TEST_IDLE_REFETCH_INTERVAL, + }) +} + +/** One earlier run with its report; null `runId` reads nothing. */ +export function useWorkflowTestRun(workspaceId: string, name: string, runId: string | null) { + return useQuery({ + queryKey: workflowTestKeys.run(workspaceId, name, runId ?? ''), + queryFn: ({ signal }) => + requestJson(getWorkflowTestRunContract, { + params: { id: workspaceId, name, runId: runId ?? '' }, + signal, + }), + enabled: Boolean(workspaceId && name && runId), + staleTime: WORKFLOW_TEST_STALE_TIME, + select: (data) => data.run, + }) +} + +/** Refetches one test after its file changes, so its cases follow the edit. */ +export function useRefreshWorkflowTest(workspaceId: string, name: string) { + const queryClient = useQueryClient() + return useCallback(() => { + queryClient.invalidateQueries({ queryKey: workflowTestKeys.lists() }) + queryClient.invalidateQueries({ queryKey: workflowTestKeys.test(workspaceId, name) }) + }, [queryClient, workspaceId, name]) +} + +export function useRunWorkflowTests(workspaceId: string) { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: (body: { version: WorkflowTestVersion; names?: string[] }) => + requestJson(runWorkflowTestsContract, { params: { id: workspaceId }, body }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: workflowTestKeys.lists() }) + queryClient.invalidateQueries({ queryKey: workflowTestKeys.details() }) + }, + }) +} diff --git a/apps/sim/lib/api/contracts/mothership-management-tools.test.ts b/apps/sim/lib/api/contracts/mothership-management-tools.test.ts index e64a8040214..acfa532ffca 100644 --- a/apps/sim/lib/api/contracts/mothership-management-tools.test.ts +++ b/apps/sim/lib/api/contracts/mothership-management-tools.test.ts @@ -37,6 +37,15 @@ const examples = { { action: 'set', content: 'title: Support\nblocks: []' }, { action: 'set', content: 'title: Support', expectedRevision: 'r1' }, ], + tests: [ + { action: 'create', name: 'triage', title: 'Triage posts the firing alarm' }, + { action: 'update', name: 'triage', description: null }, + { action: 'list', version: 'draft' }, + { action: 'get', name: 'triage', version: 'deployed' }, + { action: 'run', version: 'draft' }, + { action: 'run', version: 'draft', names: ['triage'], only: ['Triage > posts a summary'] }, + { action: 'delete', name: 'triage' }, + ], } describe('management tool provider contract', () => { diff --git a/apps/sim/lib/api/contracts/mothership-management-tools.ts b/apps/sim/lib/api/contracts/mothership-management-tools.ts index e2b839b55af..21adcee8ed7 100644 --- a/apps/sim/lib/api/contracts/mothership-management-tools.ts +++ b/apps/sim/lib/api/contracts/mothership-management-tools.ts @@ -1,5 +1,6 @@ import { z } from 'zod' import { mothershipDashboardsInputSchema } from '@/lib/api/contracts/mothership-dashboards' +import { mothershipTestsInputSchema } from '@/lib/api/contracts/mothership-tests' import { createWorkspaceInputSchema } from '@/lib/workspaces/create-input' import { organizationSearchSourcesInputSchema } from './mothership-search-sources' import { mothershipSettingsInputSchema } from './mothership-settings' @@ -18,6 +19,14 @@ export const managementToolContracts = [ 'Read and save the selected workspace’s single dashboard, validated YAML over live tables. Load the create-dashboard skill for the schema. get returns content and revision, or nulls when the workspace has no dashboard yet; set with no revision creates it. Replacing an existing dashboard requires expectedRevision from get, so a concurrent edit is never overwritten. Use open_resource with type dashboard to show the result.', inputSchema: mothershipDashboardsInputSchema, }, + { + id: 'tests', + route: 'sim', + scope: 'all', + description: + 'Create and run the selected workspace\u2019s workflow tests. create takes a name, a one-line title for the concern, and an optional description, and returns tests/.test.js; write the cases into that file with the file tools. A test file is plain vitest: import { describe, it, expect, vi } from "vitest" and { runWorkflow, mockBlock, mockTool, spyOnBlock } from "sim:test", one top-level describe, an it per case. runWorkflow(name, input) runs a workflow and returns { output } (pass { trigger: \"Trigger block name\" } as a third argument when it has several triggers); mockBlock(blockName) returns a vi.fn whose value replaces that block\u2019s output and records its inputs; mockTool(toolId) or mockTool(agentBlockName, toolId) answers an Agent\u2019s calls to that tool the same way while the model still runs, naming built-in tools by id (slack_message), MCP tools by server as mockTool({ mcp: "Server name", tool: "tool_name" }), and custom tools by title as mockTool({ customTool: "Title" }); .mockSampleOutput({ ...overrides }) on either mock returns a placeholder output shaped like the real one, with your fields merged in; await expect(value).toMatchRubric(rubric) asks a model judge for pass or fail. Every write is checked and refused if the file does not load. run takes a version (draft while editing, deployed before shipping), waits, and returns each file\u2019s failures with line numbers and messages. list and get report status; update changes title or description.', + inputSchema: mothershipTestsInputSchema, + }, { id: 'workspaces', route: 'sim', diff --git a/apps/sim/lib/api/contracts/mothership-resource-tools.ts b/apps/sim/lib/api/contracts/mothership-resource-tools.ts index c7187963192..d4224c0724b 100644 --- a/apps/sim/lib/api/contracts/mothership-resource-tools.ts +++ b/apps/sim/lib/api/contracts/mothership-resource-tools.ts @@ -10,7 +10,7 @@ export const openResourceInputSchema = z.strictObject({ resources: z .array( z.strictObject({ - type: z.enum(['workflow', 'table', 'knowledgebase', 'file', 'dashboard', 'log']), + type: z.enum(['workflow', 'table', 'knowledgebase', 'file', 'dashboard', 'log', 'test']), id: z.string().trim().min(1), viewId: z .string() @@ -26,7 +26,7 @@ export const openResourceInputSchema = z.strictObject({ export const openResourceOutputSchema = z.object({ resources: z.array( z.object({ - type: z.enum(['workflow', 'table', 'knowledgebase', 'file', 'dashboard', 'log']), + type: z.enum(['workflow', 'table', 'knowledgebase', 'file', 'dashboard', 'log', 'test']), id: z.string(), title: z.string(), viewId: z.string().optional(), diff --git a/apps/sim/lib/api/contracts/mothership-tests.ts b/apps/sim/lib/api/contracts/mothership-tests.ts new file mode 100644 index 00000000000..074643057c9 --- /dev/null +++ b/apps/sim/lib/api/contracts/mothership-tests.ts @@ -0,0 +1,65 @@ +import { z } from 'zod' + +const scope = z.object({ workspaceId: z.string().min(1).max(100).optional() }) +/** A test file name: `tests/.test.js`. `run` is taken by the run endpoint beside the names. */ +export const TEST_NAME_PATTERN = /^(?!run$)[a-z0-9][a-z0-9-]{0,79}$/ + +const nameSchema = z + .string() + .trim() + .regex( + TEST_NAME_PATTERN, + 'Use a lowercase name of letters, numbers, and dashes, like billing-routing (not "run")' + ) + .describe('Test file name: tests/.test.js, lowercase letters, numbers, and dashes.') +const versionSchema = z + .enum(['draft', 'deployed']) + .describe( + 'Which version of the workflows to test: draft while editing, deployed before shipping.' + ) + +/** Create, describe, and run the workspace's workflow tests. Cases live in `tests/.test.js`. */ +export const mothershipTestsInputSchema = z.discriminatedUnion('action', [ + scope + .extend({ + action: z.literal('create'), + name: nameSchema, + title: z + .string() + .trim() + .min(1) + .max(200) + .describe('The concern this file tests, in one line.'), + description: z.string().max(2000).optional(), + }) + .strict(), + scope + .extend({ + action: z.literal('update'), + name: nameSchema, + title: z.string().trim().min(1).max(200).optional(), + description: z.string().max(2000).nullable().optional(), + }) + .strict(), + scope.extend({ action: z.literal('list'), version: versionSchema }).strict(), + scope.extend({ action: z.literal('get'), name: nameSchema, version: versionSchema }).strict(), + scope + .extend({ + action: z.literal('run'), + version: versionSchema, + names: z + .array(nameSchema) + .min(1) + .max(100) + .optional() + .describe('Omit to run every test file.'), + only: z + .array(z.string().min(1).max(500)) + .min(1) + .max(100) + .optional() + .describe('With exactly one name: the "describe > it" test names to run in it.'), + }) + .strict(), + scope.extend({ action: z.literal('delete'), name: nameSchema }).strict(), +]) diff --git a/apps/sim/lib/api/contracts/workflow-tests.ts b/apps/sim/lib/api/contracts/workflow-tests.ts new file mode 100644 index 00000000000..8863ff6823b --- /dev/null +++ b/apps/sim/lib/api/contracts/workflow-tests.ts @@ -0,0 +1,120 @@ +import { z } from 'zod' +import { workspaceIdSchema } from '@/lib/api/contracts/primitives' +import { type ContractJsonResponse, defineRouteContract } from '@/lib/api/contracts/types' +import { testCaseStatusSchema, testReportSchema } from '@/lib/workflow-tests/protocol' + +const versionSchema = z.enum(['draft', 'deployed']) +const testNameSchema = z + .string() + .min(1) + .max(80, 'Test names are at most 80 characters, like billing-routing') + +const workspaceParams = z.object({ id: workspaceIdSchema }) +const testParams = z.object({ id: workspaceIdSchema, name: testNameSchema }) +/** Omitted, runs of both versions are read. */ +const versionQuery = z.object({ version: versionSchema.optional() }) + +const runSummarySchema = z.object({ + id: z.string(), + status: z.enum(['running', 'passed', 'failed', 'error']), + passed: z.number().int(), + failed: z.number().int(), + skipped: z.number().int(), + error: z.string().nullable(), + triggeredByUserId: z.string().nullable(), + version: versionSchema, + sourceChanged: z.boolean(), + ranAgainst: z.array( + z.object({ + workflowId: z.string(), + name: z.string().nullable(), + version: z.number().int().nullable(), + draft: z.boolean(), + liveVersion: z.number().int().nullable(), + stale: z.boolean(), + executionId: z.string().nullable(), + }) + ), + current: z.boolean(), + startedAt: z.string(), + completedAt: z.string().nullable(), +}) + +/** One run with its report, and each case's status while it goes. */ +const runDetailSchema = runSummarySchema.extend({ + report: testReportSchema.nullable(), + /** Each case's status by `describe > it` path while the run goes; null before any. */ + progress: z.record(z.string(), testCaseStatusSchema).nullable(), +}) + +const testRecordSchema = z.object({ + id: z.string(), + name: z.string(), + title: z.string(), + description: z.string().nullable(), + path: z.string(), + fileId: z.string(), + caseCount: z.number().int(), + createdByUserId: z.string().nullable(), + createdAt: z.string(), + status: z.enum(['passing', 'failing', 'error', 'running', 'changed', 'not_run']), + recentRuns: z.array(runSummarySchema), + updatedAt: z.string(), +}) + +const caseSchema = z.object({ + path: z.array(z.string()), + mode: z.enum(['run', 'only', 'skip']), + line: z.number().int().positive(), + endLine: z.number().int().positive(), +}) + +export const listWorkflowTestsContract = defineRouteContract({ + method: 'GET', + path: '/api/workspaces/[id]/tests', + params: workspaceParams, + query: versionQuery, + response: { mode: 'json', schema: z.object({ tests: z.array(testRecordSchema) }) }, +}) + +export type WorkflowTestRecord = ContractJsonResponse< + typeof listWorkflowTestsContract +>['tests'][number] + +export const getWorkflowTestContract = defineRouteContract({ + method: 'GET', + path: '/api/workspaces/[id]/tests/[name]', + params: testParams, + query: versionQuery, + response: { + mode: 'json', + schema: z.object({ + test: testRecordSchema.extend({ cases: z.array(caseSchema) }), + latestRun: runDetailSchema.nullable(), + history: z.array(runSummarySchema), + }), + }, +}) +export type WorkflowTestDetail = ContractJsonResponse + +export const getWorkflowTestRunContract = defineRouteContract({ + method: 'GET', + path: '/api/workspaces/[id]/tests/[name]/runs/[runId]', + params: testParams.extend({ runId: z.string().min(1).max(64) }), + response: { mode: 'json', schema: z.object({ run: runDetailSchema }) }, +}) + +export const runWorkflowTestsContract = defineRouteContract({ + method: 'POST', + path: '/api/workspaces/[id]/tests/run', + params: workspaceParams, + body: z.object({ + version: versionSchema, + names: z.array(testNameSchema).min(1).max(100).optional(), + only: z.array(z.string().min(1).max(500)).min(1).max(100).optional(), + }), + response: { + mode: 'json', + schema: z.object({ runs: z.array(z.object({ runId: z.string(), name: z.string() })) }), + }, +}) diff --git a/apps/sim/lib/api/contracts/workspace-files.ts b/apps/sim/lib/api/contracts/workspace-files.ts index 22453430e7e..d8b58df82a8 100644 --- a/apps/sim/lib/api/contracts/workspace-files.ts +++ b/apps/sim/lib/api/contracts/workspace-files.ts @@ -162,7 +162,7 @@ export const workspaceFileRecordSchema = z.object({ contentUpdatedAt: z.coerce.date().nullable().optional(), revision: z.string().optional(), storageContext: z.enum(['workspace', 'mothership']).optional(), - vfsNamespace: z.literal('uploads').optional(), + vfsNamespace: z.enum(['uploads', 'tests']).optional(), share: shareRecordSchema.nullable().optional(), }) diff --git a/apps/sim/lib/billing/usage-sources.ts b/apps/sim/lib/billing/usage-sources.ts index 0e7faeae394..375038684c6 100644 --- a/apps/sim/lib/billing/usage-sources.ts +++ b/apps/sim/lib/billing/usage-sources.ts @@ -10,6 +10,7 @@ export const INTERNAL_USAGE_LOG_SOURCES = [ 'enrichment', 'voice-output', 'api-tool', + 'workflow-test', ] as const export type InternalUsageLogSource = (typeof INTERNAL_USAGE_LOG_SOURCES)[number] @@ -41,12 +42,13 @@ const INTERNAL_TO_BILLING_SOURCE = { enrichment: 'enrichment', 'voice-output': 'voice-output', 'api-tool': 'api-tool', + 'workflow-test': 'workflow', } as const satisfies Record const INTERNAL_USAGE_LOG_SOURCE_SET = new Set(INTERNAL_USAGE_LOG_SOURCES) const BILLING_TO_INTERNAL_SOURCES = { - workflow: ['workflow'], + workflow: ['workflow', 'workflow-test'], wand: ['wand'], 'sim-chat': ['copilot', 'workspace-chat'], mcp_copilot: ['mcp_copilot'], diff --git a/apps/sim/lib/core/config/env.ts b/apps/sim/lib/core/config/env.ts index 261f530f93a..0909964bbbd 100644 --- a/apps/sim/lib/core/config/env.ts +++ b/apps/sim/lib/core/config/env.ts @@ -589,6 +589,7 @@ export const env = createEnv({ MSHIP_PLAN_MODE: z.boolean().optional(), DASHBOARDS: z.boolean().optional(), PROJECT_API_ENABLED: z.boolean().optional(), // Fallback for the `projects` feature flag off AppConfig + WORKFLOW_TESTS: z.boolean().optional(), // Fallback for the `workflow-tests` feature flag off AppConfig MSHIP_MODEL_SELECTOR: z.boolean().optional(), INBOX_ENABLED: z.boolean().optional(), // Enable inbox (Sim Mailer) on self-hosted (bypasses hosted requirements) SANDBOXES_ENABLED: z.boolean().optional(), // Enable custom sandboxes on self-hosted (bypasses hosted requirements) diff --git a/apps/sim/lib/core/config/feature-flags.ts b/apps/sim/lib/core/config/feature-flags.ts index 13849510a84..f3f648f2bfc 100644 --- a/apps/sim/lib/core/config/feature-flags.ts +++ b/apps/sim/lib/core/config/feature-flags.ts @@ -51,6 +51,11 @@ const FEATURE_FLAGS = { 'Enable dashboard resources, rendering, analytics, and Mothership authoring. Supports global and organization rollout; disabled by default.', fallback: 'DASHBOARDS', }, + 'workflow-tests': { + description: + 'Enable workflow tests: the workspace Tests page, tests/.test.js files, and the Sim tests tool. Supports global and organization rollout; disabled by default.', + fallback: 'WORKFLOW_TESTS', + }, 'mothership-model-selector': { description: 'Show the Mothership model selector, model-specific effort levels, and Fast for supported ' + diff --git a/apps/sim/lib/execution/isolated-vm-worker.cjs b/apps/sim/lib/execution/isolated-vm-worker.cjs index a5eeb2e6dd0..4d2ff9ce5f8 100644 --- a/apps/sim/lib/execution/isolated-vm-worker.cjs +++ b/apps/sim/lib/execution/isolated-vm-worker.cjs @@ -23,6 +23,7 @@ const SANDBOX_BUNDLE_FILES = { pptxgenjs: 'pptxgenjs.cjs', docx: 'docx.cjs', 'pdf-lib': 'pdf-lib.cjs', + 'vitest-expect': 'vitest-expect.cjs', } const bundleSourceCache = new Map() const activeIsolates = new Map() @@ -600,6 +601,34 @@ async function executeCode(request, executionId) { * executes user code, then runs `finalize` (must return a Uint8Array). The * resulting bytes are returned as base64 in `bytesBase64`. */ +/** + * Evaluates task user code as an ES module that may import only the task's own modules + * (`userModule.modules`: specifier -> source). Failures throw, so `executeTask`'s catch + * classifies them exactly as it does a script's (user error, timeout, cancellation, OOM). + */ +async function evaluateUserModule(isolate, context, code, userModule, timeoutMs, releaseables) { + const provided = new Map() + for (const [specifier, source] of Object.entries(userModule.modules)) { + const providedModule = await isolate.compileModule(source, { + filename: `sandbox/modules/${specifier}.js`, + }) + releaseables.push(providedModule) + await providedModule.instantiate(context, (dependency) => { + throw new Error(`Sandbox module "${specifier}" cannot import "${dependency}"`) + }) + provided.set(specifier, providedModule) + } + const importable = [...provided.keys()].map((specifier) => `"${specifier}"`).join(' and ') + const entry = await isolate.compileModule(code, { filename: userModule.filename }) + releaseables.push(entry) + await entry.instantiate(context, (specifier) => { + const match = provided.get(specifier) + if (!match) throw new Error(`Cannot import "${specifier}": only ${importable} can be imported`) + return match + }) + await entry.evaluate({ timeout: timeoutMs, promise: true }) +} + async function executeTask(request, executionId) { const { code, timeoutMs, task } = request const stdoutChunks = [] @@ -981,7 +1010,12 @@ async function executeTask(request, executionId) { timings.harden = Date.now() - tPhase tPhase = Date.now() - const wrappedUserCode = ` + let userResult + if (task.userModule) { + await evaluateUserModule(isolate, context, code, task.userModule, timeoutMs, releaseables) + userResult = { success: true } + } else { + const wrappedUserCode = ` (async () => { try { await (async () => { @@ -1000,21 +1034,23 @@ async function executeTask(request, executionId) { } })() ` - const userScript = await isolate.compileScript(wrappedUserCode, { - filename: 'user-function.js', - }) - releaseables.push(userScript) - const userResultJson = await userScript.run(context, { timeout: timeoutMs, promise: true }) + const userScript = await isolate.compileScript(wrappedUserCode, { + filename: 'user-function.js', + }) + releaseables.push(userScript) + const userResultJson = await userScript.run(context, { timeout: timeoutMs, promise: true }) + try { + userResult = JSON.parse(userResultJson) + } catch { + userResult = { + success: false, + errorInfo: { message: 'Invalid user result', name: 'Error' }, + } + } + } timings.userCode = Date.now() - tPhase tPhase = Date.now() - let userResult - try { - userResult = JSON.parse(userResultJson) - } catch { - userResult = { success: false, errorInfo: { message: 'Invalid user result', name: 'Error' } } - } - if (!userResult.success) { timings.total = Date.now() - tStart return { diff --git a/apps/sim/lib/execution/isolated-vm.ts b/apps/sim/lib/execution/isolated-vm.ts index d257c08c8fa..8292b05beba 100644 --- a/apps/sim/lib/execution/isolated-vm.ts +++ b/apps/sim/lib/execution/isolated-vm.ts @@ -58,6 +58,8 @@ interface IsolatedVMTaskRequest { bootstrap: string brokers: string[] finalize: string + /** Evaluate `code` as an ES module named `filename` that may import only `modules`. */ + userModule?: { filename: string; modules: Record } } /** diff --git a/apps/sim/lib/execution/sandbox/bundles/build.ts b/apps/sim/lib/execution/sandbox/bundles/build.ts index 8d380bb4276..41d2133bae9 100644 --- a/apps/sim/lib/execution/sandbox/bundles/build.ts +++ b/apps/sim/lib/execution/sandbox/bundles/build.ts @@ -54,6 +54,7 @@ interface BundleSpec { } const POLYFILLS_PATH = join(HERE, '_polyfills.ts') +const EVENT_POLYFILLS_PATH = join(HERE, 'event-polyfills.ts') const POLYFILL_PRELUDE = ` // Isolate-side polyfills must execute BEFORE any other import (process/browser // captures setTimeout at module-init time). Keep this as the first import. @@ -93,6 +94,65 @@ ${POLYFILL_PRELUDE} import PptxGenJS from 'pptxgenjs' globalThis.__bundles = globalThis.__bundles || {} globalThis.__bundles['pptxgenjs'] = PptxGenJS +`, + }, + { + /** + * Vitest's own `expect` and spies for workflow tests. `vitest` itself can't be bundled for + * an isolate (its expect is entangled with the runner's worker RPC), so this builds the + * standalone packages vitest is made of the way vitest's `createExpect` assembles them. + */ + name: 'vitest-expect', + outFile: 'vitest-expect.cjs', + entry: ` +import '${POLYFILLS_PATH}' +import '${EVENT_POLYFILLS_PATH}' +import { disableDefaultColors } from 'tinyrainbow' +import * as chai from 'chai' +import { + ASYMMETRIC_MATCHERS_OBJECT, + JestAsymmetricMatchers, + JestChaiExpect, + JestExtend, + getState, + setState, +} from '@vitest/expect' +import { fn, isMockFunction, spyOn } from '@vitest/spy' + +disableDefaultColors() +chai.use(JestExtend) +chai.use(JestChaiExpect) +chai.use(JestAsymmetricMatchers) + +function createExpect() { + const expect = (value, message) => { + const { assertionCalls } = getState(expect) + setState({ assertionCalls: assertionCalls + 1 }, expect) + return chai.expect(value, message) + } + Object.assign(expect, chai.expect) + Object.assign(expect, globalThis[ASYMMETRIC_MATCHERS_OBJECT]) + expect.getState = () => getState(expect) + expect.setState = (state) => setState(state, expect) + setState( + { + assertionCalls: 0, + isExpectingAssertions: false, + isExpectingAssertionsError: null, + expectedAssertionsNumber: null, + expectedAssertionsNumberErrorGen: null, + }, + expect + ) + expect.assert = chai.assert + expect.extend = (matchers) => chai.expect.extend(expect, matchers) + expect.unreachable = (message) => + chai.assert.fail('expected' + (message ? ' "' + message + '" ' : ' ') + 'not to be reached') + return expect +} + +globalThis.__bundles = globalThis.__bundles || {} +globalThis.__bundles['vitest-expect'] = { createExpect, fn, spyOn, isMockFunction } `, }, ] diff --git a/apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts b/apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts new file mode 100644 index 00000000000..1eb21f4dbfc --- /dev/null +++ b/apps/sim/lib/execution/sandbox/bundles/event-polyfills.ts @@ -0,0 +1,44 @@ +/** + * chai 6 announces plugin registration through a module-level `EventTarget` and an `Event` + * subclass. A bare isolate has neither, so the vitest-expect bundle imports this first. + */ + +type Listener = (event: { type: string }) => void + +if (!('Event' in globalThis)) { + Reflect.set( + globalThis, + 'Event', + class Event { + readonly type: string + constructor(type: string) { + this.type = type + } + } + ) +} + +if (!('EventTarget' in globalThis)) { + Reflect.set( + globalThis, + 'EventTarget', + class EventTarget { + readonly #listeners = new Map>() + + addEventListener(type: string, listener: Listener): void { + const listeners = this.#listeners.get(type) ?? new Set() + listeners.add(listener) + this.#listeners.set(type, listeners) + } + + removeEventListener(type: string, listener: Listener): void { + this.#listeners.get(type)?.delete(listener) + } + + dispatchEvent(event: { type: string }): boolean { + for (const listener of this.#listeners.get(event.type) ?? []) listener(event) + return true + } + } + ) +} diff --git a/apps/sim/lib/execution/sandbox/bundles/verify.test.ts b/apps/sim/lib/execution/sandbox/bundles/verify.test.ts index 4e0cce4cac0..4fb1134f0cb 100644 --- a/apps/sim/lib/execution/sandbox/bundles/verify.test.ts +++ b/apps/sim/lib/execution/sandbox/bundles/verify.test.ts @@ -30,6 +30,23 @@ describe('sandbox bundles', () => { expect(typeof pdfLib.StandardFonts).toBe('object') }) + it('vitest-expect evaluates in a bare context and fails with uncolored vitest messages', () => { + const bundle = loadCheckedInBundle('vitest-expect') as { + createExpect: () => (value: unknown) => { toEqual: (expected: unknown) => void } + fn: () => unknown + } + expect(typeof bundle.fn).toBe('function') + const sandboxExpect = bundle.createExpect() + let message = '' + try { + sandboxExpect({ channel: '#support' }).toEqual({ channel: '#billing' }) + } catch (error) { + message = (error as Error).message + } + expect(message).toMatch(/channel: '#support'.*to deeply equal.*channel: '#billing'/) + expect(message).not.toContain('\u001b[') + }) + it('pptxgenjs evaluates in a bare context and exposes its constructor', () => { const source = readFileSync(new URL('./pptxgenjs.cjs', import.meta.url), 'utf-8') expect(typeof evaluateSandboxBundle(source, 'pptxgenjs')).toBe('function') diff --git a/apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs b/apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs new file mode 100644 index 00000000000..25215c0afe2 --- /dev/null +++ b/apps/sim/lib/execution/sandbox/bundles/vitest-expect.cjs @@ -0,0 +1,90 @@ +// sandbox bundle: vitest-expect +// generated by apps/sim/lib/execution/sandbox/bundles/build.ts +// do not edit by hand. run `bun run build:sandbox-bundles` to regenerate. +(()=>{var Y0=globalThis;if(typeof Y0.global>"u")Y0.global=globalThis;if(typeof Y0.__require>"u")Y0.__require=(Z)=>{throw Error(`Dynamic require of "${Z}" is not supported in the sandbox`)};if(!("Event"in globalThis))Reflect.set(globalThis,"Event",class{type;constructor(Q){this.type=Q}});if(!("EventTarget"in globalThis))Reflect.set(globalThis,"EventTarget",class{#Z=new Map;addEventListener(Q,X){let W=this.#Z.get(Q)??new Set;W.add(X),this.#Z.set(Q,W)}removeEventListener(Q,X){this.#Z.get(Q)?.delete(X)}dispatchEvent(Q){for(let X of this.#Z.get(Q.type)??[])X(Q);return!0}});var a0={reset:[0,0],bold:[1,22,"\x1B[22m\x1B[1m"],dim:[2,22,"\x1B[22m\x1B[2m"],italic:[3,23],underline:[4,24],inverse:[7,27],hidden:[8,28],strikethrough:[9,29],black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],gray:[90,39],bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],blackBright:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39],bgBlackBright:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]};function c9(Z){return String(Z)}c9.open="";c9.close="";function _5(){let Z={isColorSupported:!1,reset:c9};for(let Q in a0)Z[Q]=c9;return Z}function E5(){let Z=typeof process<"u"?process:void 0,Q=(Z==null?void 0:Z.env)||{},X=Q.FORCE_TTY!=="false",W=(Z==null?void 0:Z.argv)||[];return!(("NO_COLOR"in Q)||W.includes("--no-color"))&&(("FORCE_COLOR"in Q)||W.includes("--color")||(Z==null?void 0:Z.platform)==="win32"||X&&Q.TERM!=="dumb"||("CI"in Q))||typeof window<"u"&&!!window.chrome}function x5({force:Z}={}){let Q=Z||E5(),X=(G,U,z,Y)=>{let K="",B=0;do K+=G.substring(B,Y)+z,B=Y+U.length,Y=G.indexOf(U,B);while(~Y);return K+G.substring(B)},W=(G,U,z=G)=>{let Y=(K)=>{let B=String(K),P=B.indexOf(U,G.length);return~P?G+X(B,U,z,P)+U:G+B+U};return Y.open=G,Y.close=U,Y},$={isColorSupported:Q},J=(G)=>`\x1B[${G}m`;for(let G in a0){let U=a0[G];$[G]=Q?W(J(U[0]),J(U[1]),U[2]):c9}return $}var b6=x5();function u6(){Object.assign(b6,_5())}var f=b6;var q1=Object.defineProperty,v5=(Z,Q,X)=>(Q in Z)?q1(Z,Q,{enumerable:!0,configurable:!0,writable:!0,value:X}):Z[Q]=X,R=(Z,Q)=>q1(Z,"name",{value:Q,configurable:!0}),H1=(Z,Q)=>{for(var X in Q)q1(Z,X,{get:Q[X],enumerable:!0})},o6=(Z,Q,X)=>v5(Z,typeof Q!=="symbol"?Q+"":Q,X),z9={};H1(z9,{addChainableMethod:()=>k1,addLengthGuard:()=>r9,addMethod:()=>C1,addProperty:()=>L1,checkError:()=>X9,compareByInspect:()=>K0,eql:()=>O7,events:()=>N0,expectTypes:()=>X7,flag:()=>k,getActual:()=>H0,getMessage:()=>D1,getName:()=>D0,getOperator:()=>E1,getOwnEnumerableProperties:()=>_1,getOwnEnumerablePropertySymbols:()=>j1,getPathInfo:()=>O1,hasProperty:()=>P0,inspect:()=>j,isNaN:()=>B0,isNumeric:()=>l,isProxyEnabled:()=>o9,isRegExp:()=>V0,objDisplay:()=>k9,overwriteChainableMethod:()=>S1,overwriteMethod:()=>A1,overwriteProperty:()=>T1,proxify:()=>y9,test:()=>P1,transferFlags:()=>K9,type:()=>h});var X9={};H1(X9,{compatibleConstructor:()=>s6,compatibleInstance:()=>a6,compatibleMessage:()=>t6,getConstructorName:()=>e6,getMessage:()=>Z7});function q0(Z){return Z instanceof Error||Object.prototype.toString.call(Z)==="[object Error]"}R(q0,"isErrorInstance");function r6(Z){return Object.prototype.toString.call(Z)==="[object RegExp]"}R(r6,"isRegExp");function a6(Z,Q){return q0(Q)&&Z===Q}R(a6,"compatibleInstance");function s6(Z,Q){if(q0(Q))return Z.constructor===Q.constructor||Z instanceof Q.constructor;else if((typeof Q==="object"||typeof Q==="function")&&Q.prototype)return Z.constructor===Q||Z instanceof Q;return!1}R(s6,"compatibleConstructor");function t6(Z,Q){let X=typeof Z==="string"?Z:Z.message;if(r6(Q))return Q.test(X);else if(typeof Q==="string")return X.indexOf(Q)!==-1;return!1}R(t6,"compatibleMessage");function e6(Z){let Q=Z;if(q0(Z))Q=Z.constructor.name;else if(typeof Z==="function"){if(Q=Z.name,Q==="")Q=new Z().name||Q}return Q}R(e6,"getConstructorName");function Z7(Z){let Q="";if(Z&&Z.message)Q=Z.message;else if(typeof Z==="string")Q=Z;return Q}R(Z7,"getMessage");function k(Z,Q,X){let W=Z.__flags||(Z.__flags=Object.create(null));if(arguments.length===3)W[Q]=X;else return W[Q]}R(k,"flag");function P1(Z,Q){let X=k(Z,"negate"),W=Q[0];return X?!W:W}R(P1,"test");function h(Z){if(typeof Z>"u")return"undefined";if(Z===null)return"null";let Q=Z[Symbol.toStringTag];if(typeof Q==="string")return Q;return Object.prototype.toString.call(Z).slice(8,-1)}R(h,"type");var h5="captureStackTrace"in Error,Q7=class Z extends Error{constructor(Q="Unspecified AssertionError",X,W){super(Q);if(o6(this,"message"),this.message=Q,h5)Error.captureStackTrace(this,W||Z);for(let $ in X)if(!($ in this))this[$]=X[$]}get name(){return"AssertionError"}get ok(){return!1}toJSON(Q){return{...this,name:this.name,message:this.message,ok:!1,stack:Q!==!1?this.stack:void 0}}};R(Q7,"AssertionError");var _=Q7;function X7(Z,Q){let X=k(Z,"message"),W=k(Z,"ssfi");X=X?X+": ":"",Z=k(Z,"object"),Q=Q.map(function(G){return G.toLowerCase()}),Q.sort();let $=Q.map(function(G,U){let z=~["a","e","i","o","u"].indexOf(G.charAt(0))?"an":"a";return(Q.length>1&&U===Q.length-1?"or ":"")+z+" "+G}).join(", "),J=h(Z).toLowerCase();if(!Q.some(function(G){return J===G}))throw new _(X+"object tested must be "+$+", but "+J+" given",void 0,W)}R(X7,"expectTypes");function H0(Z,Q){return Q.length>4?Q[4]:Z._obj}R(H0,"getActual");var f6={bold:["1","22"],dim:["2","22"],italic:["3","23"],underline:["4","24"],inverse:["7","27"],hidden:["8","28"],strike:["9","29"],black:["30","39"],red:["31","39"],green:["32","39"],yellow:["33","39"],blue:["34","39"],magenta:["35","39"],cyan:["36","39"],white:["37","39"],brightblack:["30;1","39"],brightred:["31;1","39"],brightgreen:["32;1","39"],brightyellow:["33;1","39"],brightblue:["34;1","39"],brightmagenta:["35;1","39"],brightcyan:["36;1","39"],brightwhite:["37;1","39"],grey:["90","39"]},y5={special:"cyan",number:"yellow",bigint:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",symbol:"green",date:"magenta",regexp:"red"},v9="…";function W7(Z,Q){let X=f6[y5[Q]]||f6[Q]||"";if(!X)return String(Z);return`\x1B[${X[0]}m${String(Z)}\x1B[${X[1]}m`}R(W7,"colorise");function $7({showHidden:Z=!1,depth:Q=2,colors:X=!1,customInspect:W=!0,showProxy:$=!1,maxArrayLength:J=1/0,breakLength:G=1/0,seen:U=[],truncate:z=1/0,stylize:Y=String}={},K){let B={showHidden:Boolean(Z),depth:Number(Q),colors:Boolean(X),customInspect:Boolean(W),showProxy:Boolean($),maxArrayLength:Number(J),breakLength:Number(G),truncate:Number(z),seen:U,inspect:K,stylize:Y};if(B.colors)B.stylize=W7;return B}R($7,"normaliseOptions");function J7(Z){return Z>="\uD800"&&Z<="\uDBFF"}R(J7,"isHighSurrogate");function N9(Z,Q,X=v9){Z=String(Z);let W=X.length,$=Z.length;if(W>Q&&$>W)return X;if($>Q&&$>W){let J=Q-W;if(J>0&&J7(Z[J-1]))J=J-1;return`${Z.slice(0,J)}${X}`}return Z}R(N9,"truncate");function J9(Z,Q,X,W=", "){X=X||Q.inspect;let $=Z.length;if($===0)return"";let J=Q.truncate,G="",U="",z="";for(let Y=0;Y<$;Y+=1){let K=Y+1===Z.length,B=Y+2===Z.length;z=`${v9}(${Z.length-Y})`;let P=Z[Y];Q.truncate=J-G.length-(K?0:W.length);let w=U||X(P,Q)+(K?"":W),N=G.length+w.length,H=N+z.length;if(K&&N>J&&G.length+z.length<=J)break;if(!K&&!B&&H>J)break;if(U=K?"":X(Z[Y+1],Q)+(B?"":W),!K&&B&&H>J&&N+U.length>J)break;if(G+=w,!K&&!B&&N+U.length>=J){z=`${v9}(${Z.length-Y-1})`;break}z=""}return`${G}${z}`}R(J9,"inspectList");function G7(Z){if(Z.match(/^[a-zA-Z_][a-zA-Z_0-9]*$/))return Z;return JSON.stringify(Z).replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'")}R(G7,"quoteComplexKey");function h9([Z,Q],X){if(X.truncate-=2,typeof Z==="string")Z=G7(Z);else if(typeof Z!=="number")Z=`[${X.inspect(Z,X)}]`;return X.truncate-=Z.length,Q=X.inspect(Q,X),`${Z}: ${Q}`}R(h9,"inspectProperty");function Y7(Z,Q){let X=Object.keys(Z).slice(Z.length);if(!Z.length&&!X.length)return"[]";Q.truncate-=4;let W=J9(Z,Q);Q.truncate-=W.length;let $="";if(X.length)$=J9(X.map((J)=>[J,Z[J]]),Q,h9);return`[ ${W}${$?`, ${$}`:""} ]`}R(Y7,"inspectArray");var b5=R((Z)=>{if(typeof Buffer==="function"&&Z instanceof Buffer)return"Buffer";if(Z[Symbol.toStringTag])return Z[Symbol.toStringTag];return Z.constructor.name},"getArrayName");function q9(Z,Q){let X=b5(Z);Q.truncate-=X.length+4;let W=Object.keys(Z).slice(Z.length);if(!Z.length&&!W.length)return`${X}[]`;let $="";for(let G=0;G[G,Z[G]]),Q,h9);return`${X}[ ${$}${J?`, ${J}`:""} ]`}R(q9,"inspectTypedArray");function U7(Z,Q){let X=Z.toJSON();if(X===null)return"Invalid Date";let W=X.split("T"),$=W[0];return Q.stylize(`${$}T${N9(W[1],Q.truncate-$.length-1)}`,"date")}R(U7,"inspectDate");function Z1(Z,Q){let X=Z[Symbol.toStringTag]||"Function",W=Z.name;if(!W)return Q.stylize(`[${X}]`,"special");return Q.stylize(`[${X} ${N9(W,Q.truncate-11)}]`,"special")}R(Z1,"inspectFunction");function z7([Z,Q],X){return X.truncate-=4,Z=X.inspect(Z,X),X.truncate-=Z.length,Q=X.inspect(Q,X),`${Z} => ${Q}`}R(z7,"inspectMapEntry");function K7(Z){let Q=[];return Z.forEach((X,W)=>{Q.push([W,X])}),Q}R(K7,"mapToEntries");function B7(Z,Q){if(Z.size===0)return"Map{}";return Q.truncate-=7,`Map{ ${J9(K7(Z),Q,z7)} }`}R(B7,"inspectMap");var u5=Number.isNaN||((Z)=>Z!==Z);function Q1(Z,Q){if(u5(Z))return Q.stylize("NaN","number");if(Z===1/0)return Q.stylize("Infinity","number");if(Z===-1/0)return Q.stylize("-Infinity","number");if(Z===0)return Q.stylize(1/Z===1/0?"+0":"-0","number");return Q.stylize(N9(String(Z),Q.truncate),"number")}R(Q1,"inspectNumber");function X1(Z,Q){let X=N9(Z.toString(),Q.truncate-1);if(X!==v9)X+="n";return Q.stylize(X,"bigint")}R(X1,"inspectBigInt");function V7(Z,Q){let X=Z.toString().split("/")[2],W=Q.truncate-(2+X.length),$=Z.source;return Q.stylize(`/${N9($,W)}/${X}`,"regexp")}R(V7,"inspectRegExp");function w7(Z){let Q=[];return Z.forEach((X)=>{Q.push(X)}),Q}R(w7,"arrayFromSet");function q7(Z,Q){if(Z.size===0)return"Set{}";return Q.truncate-=7,`Set{ ${J9(w7(Z),Q)} }`}R(q7,"inspectSet");var g6=new RegExp("['\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]","g"),f5={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r","'":"\\'","\\":"\\\\"},g5=16,m5=4;function H7(Z){return f5[Z]||`\\u${`0000${Z.charCodeAt(0).toString(g5)}`.slice(-m5)}`}R(H7,"escape");function W1(Z,Q){if(g6.test(Z))Z=Z.replace(g6,H7);return Q.stylize(`'${N9(Z,Q.truncate-2)}'`,"string")}R(W1,"inspectString");function $1(Z){if("description"in Symbol.prototype)return Z.description?`Symbol(${Z.description})`:"Symbol()";return Z.toString()}R($1,"inspectSymbol");var d5=R(()=>"Promise{…}","getPromiseValue"),c5=d5;function p9(Z,Q){let X=Object.getOwnPropertyNames(Z),W=Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(Z):[];if(X.length===0&&W.length===0)return"{}";if(Q.truncate-=4,Q.seen=Q.seen||[],Q.seen.includes(Z))return"[Circular]";Q.seen.push(Z);let $=J9(X.map((U)=>[U,Z[U]]),Q,h9),J=J9(W.map((U)=>[U,Z[U]]),Q,h9);Q.seen.pop();let G="";if($&&J)G=", ";return`{ ${$}${G}${J} }`}R(p9,"inspectObject");var s0=typeof Symbol<"u"&&Symbol.toStringTag?Symbol.toStringTag:!1;function P7(Z,Q){let X="";if(s0&&s0 in Z)X=Z[s0];if(X=X||Z.constructor.name,!X||X==="_class")X="";return Q.truncate-=X.length,`${X}${p9(Z,Q)}`}R(P7,"inspectClass");function N7(Z,Q){if(Z.length===0)return"Arguments[]";return Q.truncate-=13,`Arguments[ ${J9(Z,Q)} ]`}R(N7,"inspectArguments");var l5=["stack","line","column","name","message","fileName","lineNumber","columnNumber","number","description","cause"];function D7(Z,Q){let X=Object.getOwnPropertyNames(Z).filter((G)=>l5.indexOf(G)===-1),W=Z.name;Q.truncate-=W.length;let $="";if(typeof Z.message==="string")$=N9(Z.message,Q.truncate);else X.unshift("message");if($=$?`: ${$}`:"",Q.truncate-=$.length+5,Q.seen=Q.seen||[],Q.seen.includes(Z))return"[Circular]";Q.seen.push(Z);let J=J9(X.map((G)=>[G,Z[G]]),Q,h9);return`${W}${$}${J?` { ${J} }`:""}`}R(D7,"inspectObject");function F7([Z,Q],X){if(X.truncate-=3,!Q)return`${X.stylize(String(Z),"yellow")}`;return`${X.stylize(String(Z),"yellow")}=${X.stylize(`"${Q}"`,"string")}`}R(F7,"inspectAttribute");function U0(Z,Q){return J9(Z,Q,R7,` +`)}R(U0,"inspectNodeCollection");function R7(Z,Q){switch(Z.nodeType){case 1:return N1(Z,Q);case 3:return Q.inspect(Z.data,Q);default:return Q.inspect(Z,Q)}}R(R7,"inspectNode");function N1(Z,Q){let X=Z.getAttributeNames(),W=Z.tagName.toLowerCase(),$=Q.stylize(`<${W}`,"special"),J=Q.stylize(">","special"),G=Q.stylize(``,"special");Q.truncate-=W.length*2+5;let U="";if(X.length>0)U+=" ",U+=J9(X.map((K)=>[K,Z.getAttribute(K)]),Q,F7," ");Q.truncate-=U.length;let z=Q.truncate,Y=U0(Z.children,Q);if(Y&&Y.length>z)Y=`${v9}(${Z.children.length})`;return`${$}${U}${J}${Y}${G}`}R(N1,"inspectHTML");var p5=typeof Symbol==="function"&&typeof Symbol.for==="function",t0=p5?Symbol.for("chai/inspect"):"@@chai/inspect",e0=Symbol.for("nodejs.util.inspect.custom"),m6=new WeakMap,d6={},c6={undefined:R((Z,Q)=>Q.stylize("undefined","undefined"),"undefined"),null:R((Z,Q)=>Q.stylize("null","null"),"null"),boolean:R((Z,Q)=>Q.stylize(String(Z),"boolean"),"boolean"),Boolean:R((Z,Q)=>Q.stylize(String(Z),"boolean"),"Boolean"),number:Q1,Number:Q1,bigint:X1,BigInt:X1,string:W1,String:W1,function:Z1,Function:Z1,symbol:$1,Symbol:$1,Array:Y7,Date:U7,Map:B7,Set:q7,RegExp:V7,Promise:c5,WeakSet:R((Z,Q)=>Q.stylize("WeakSet{…}","special"),"WeakSet"),WeakMap:R((Z,Q)=>Q.stylize("WeakMap{…}","special"),"WeakMap"),Arguments:N7,Int8Array:q9,Uint8Array:q9,Uint8ClampedArray:q9,Int16Array:q9,Uint16Array:q9,Int32Array:q9,Uint32Array:q9,Float32Array:q9,Float64Array:q9,Generator:R(()=>"","Generator"),DataView:R(()=>"","DataView"),ArrayBuffer:R(()=>"","ArrayBuffer"),Error:D7,HTMLCollection:U0,NodeList:U0},i5=R((Z,Q,X,W)=>{if(t0 in Z&&typeof Z[t0]==="function")return Z[t0](Q);if(e0 in Z&&typeof Z[e0]==="function")return Z[e0](Q.depth,Q,W);if("inspect"in Z&&typeof Z.inspect==="function")return Z.inspect(Q.depth,Q);if("constructor"in Z&&m6.has(Z.constructor))return m6.get(Z.constructor)(Z,Q);if(d6[X])return d6[X](Z,Q);return""},"inspectCustom"),n5=Object.prototype.toString;function i9(Z,Q={}){let X=$7(Q,i9),{customInspect:W}=X,$=Z===null?"null":typeof Z;if($==="object")$=n5.call(Z).slice(8,-1);if($ in c6)return c6[$](Z,X);if(W&&Z){let G=i5(Z,X,$,i9);if(G){if(typeof G==="string")return G;return i9(G,X)}}let J=Z?Object.getPrototypeOf(Z):!1;if(J===Object.prototype||J===null)return p9(Z,X);if(Z&&typeof HTMLElement==="function"&&Z instanceof HTMLElement)return N1(Z,X);if("constructor"in Z){if(Z.constructor!==Object)return P7(Z,X);return p9(Z,X)}if(Z===Object(Z))return p9(Z,X);return X.stylize(String(Z),$)}R(i9,"inspect");var e={includeStack:!1,showDiff:!0,truncateThreshold:40,useProxy:!0,proxyExcludedKeys:["then","catch","inspect","toJSON"],deepEqual:null};function j(Z,Q,X,W){let $={colors:W,depth:typeof X>"u"?2:X,showHidden:Q,truncate:e.truncateThreshold?e.truncateThreshold:1/0};return i9(Z,$)}R(j,"inspect");function k9(Z){let Q=j(Z),X=Object.prototype.toString.call(Z);if(e.truncateThreshold&&Q.length>=e.truncateThreshold)if(X==="[object Function]")return!Z.name||Z.name===""?"[Function]":"[Function: "+Z.name+"]";else if(X==="[object Array]")return"[ Array("+Z.length+") ]";else if(X==="[object Object]"){let W=Object.keys(Z);return"{ Object ("+(W.length>2?W.splice(0,2).join(", ")+", ...":W.join(", "))+") }"}else return Q;else return Q}R(k9,"objDisplay");function D1(Z,Q){let X=k(Z,"negate"),W=k(Z,"object"),$=Q[3],J=H0(Z,Q),G=X?Q[2]:Q[1],U=k(Z,"message");if(typeof G==="function")G=G();return G=G||"",G=G.replace(/#\{this\}/g,function(){return k9(W)}).replace(/#\{act\}/g,function(){return k9(J)}).replace(/#\{exp\}/g,function(){return k9($)}),U?U+": "+G:G}R(D1,"getMessage");function K9(Z,Q,X){let W=Z.__flags||(Z.__flags=Object.create(null));if(!Q.__flags)Q.__flags=Object.create(null);X=arguments.length===3?X:!0;for(let $ in W)if(X||$!=="object"&&$!=="ssfi"&&$!=="lockSsfi"&&$!="message")Q.__flags[$]=W[$]}R(K9,"transferFlags");function J1(Z){if(typeof Z>"u")return"undefined";if(Z===null)return"null";let Q=Z[Symbol.toStringTag];if(typeof Q==="string")return Q;let X=8,W=-1;return Object.prototype.toString.call(Z).slice(X,W)}R(J1,"type");function F1(){this._key="chai/deep-eql__"+Math.random()+Date.now()}R(F1,"FakeMap");F1.prototype={get:R(function(Q){return Q[this._key]},"get"),set:R(function(Q,X){if(Object.isExtensible(Q))Object.defineProperty(Q,this._key,{value:X,configurable:!0})},"set")};var M7=typeof WeakMap==="function"?WeakMap:F1;function G1(Z,Q,X){if(!X||S9(Z)||S9(Q))return null;var W=X.get(Z);if(W){var $=W.get(Q);if(typeof $==="boolean")return $}return null}R(G1,"memoizeCompare");function l9(Z,Q,X,W){if(!X||S9(Z)||S9(Q))return;var $=X.get(Z);if($)$.set(Q,W);else $=new M7,$.set(Q,W),X.set(Z,$)}R(l9,"memoizeSet");var O7=n9;function n9(Z,Q,X){if(X&&X.comparator)return Y1(Z,Q,X);var W=R1(Z,Q);if(W!==null)return W;return Y1(Z,Q,X)}R(n9,"deepEqual");function R1(Z,Q){if(Z===Q)return Z!==0||1/Z===1/Q;if(Z!==Z&&Q!==Q)return!0;if(S9(Z)||S9(Q))return!1;return null}R(R1,"simpleEqual");function Y1(Z,Q,X){X=X||{},X.memoize=X.memoize===!1?!1:X.memoize||new M7;var W=X&&X.comparator,$=G1(Z,Q,X.memoize);if($!==null)return $;var J=G1(Q,Z,X.memoize);if(J!==null)return J;if(W){var G=W(Z,Q);if(G===!1||G===!0)return l9(Z,Q,X.memoize,G),G;var U=R1(Z,Q);if(U!==null)return U}var z=J1(Z);if(z!==J1(Q))return l9(Z,Q,X.memoize,!1),!1;l9(Z,Q,X.memoize,!0);var Y=I7(Z,Q,z,X);return l9(Z,Q,X.memoize,Y),Y}R(Y1,"extensiveDeepEqual");function I7(Z,Q,X,W){switch(X){case"String":case"Number":case"Boolean":case"Date":return n9(Z.valueOf(),Q.valueOf());case"Promise":case"Symbol":case"function":case"WeakMap":case"WeakSet":return Z===Q;case"Error":return M1(Z,Q,["name","message","code"],W);case"Arguments":case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"Array":return O9(Z,Q,W);case"RegExp":return L7(Z,Q);case"Generator":return C7(Z,Q,W);case"DataView":return O9(new Uint8Array(Z.buffer),new Uint8Array(Q.buffer),W);case"ArrayBuffer":return O9(new Uint8Array(Z),new Uint8Array(Q),W);case"Set":return U1(Z,Q,W);case"Map":return U1(Z,Q,W);case"Temporal.PlainDate":case"Temporal.PlainTime":case"Temporal.PlainDateTime":case"Temporal.Instant":case"Temporal.ZonedDateTime":case"Temporal.PlainYearMonth":case"Temporal.PlainMonthDay":return Z.equals(Q);case"Temporal.Duration":return Z.total("nanoseconds")===Q.total("nanoseconds");case"Temporal.TimeZone":case"Temporal.Calendar":return Z.toString()===Q.toString();default:return A7(Z,Q,W)}}R(I7,"extensiveDeepEqualByType");function L7(Z,Q){return Z.toString()===Q.toString()}R(L7,"regexpEqual");function U1(Z,Q,X){try{if(Z.size!==Q.size)return!1;if(Z.size===0)return!0}catch(J){return!1}var W=[],$=[];return Z.forEach(R(function(G,U){W.push([G,U])},"gatherEntries")),Q.forEach(R(function(G,U){$.push([G,U])},"gatherEntries")),O9(W.sort(),$.sort(),X)}R(U1,"entriesEqual");function O9(Z,Q,X){var W=Z.length;if(W!==Q.length)return!1;if(W===0)return!0;var $=-1;while(++$"u"||Z===null)return!1;return Q in Object(Z)}R(P0,"hasProperty");function k7(Z){return Z.replace(/([^\\])\[/g,"$1.[").match(/(\\\.|[^.]+?)+/g).map((W)=>{if(W==="constructor"||W==="__proto__"||W==="prototype")return{};let J=/^\[(\d+)\]$/.exec(W),G=null;if(J)G={i:parseFloat(J[1])};else G={p:W.replace(/\\([.[\]])/g,"$1")};return G})}R(k7,"parsePath");function w1(Z,Q,X){let W=Z,$=null;X=typeof X>"u"?Q.length:X;for(let J=0;J"u")W=W[G.i];else W=W[G.p];if(J===X-1)$=W}}return $}R(w1,"internalGetPathValue");function O1(Z,Q){let X=k7(Q),W=X[X.length-1],$={parent:X.length>1?w1(Z,X,X.length-1):Z,name:W.p||W.i,value:w1(Z,X)};return $.exists=P0($.parent,$.name),$}R(O1,"getPathInfo");var S7=class Z{constructor(Q,X,W,$){return o6(this,"__flags",{}),k(this,"ssfi",W||Z),k(this,"lockSsfi",$),k(this,"object",Q),k(this,"message",X),k(this,"eql",e.deepEqual||O7),y9(this)}static get includeStack(){return console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),e.includeStack}static set includeStack(Q){console.warn("Assertion.includeStack is deprecated, use chai.config.includeStack instead."),e.includeStack=Q}static get showDiff(){return console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),e.showDiff}static set showDiff(Q){console.warn("Assertion.showDiff is deprecated, use chai.config.showDiff instead."),e.showDiff=Q}static addProperty(Q,X){L1(this.prototype,Q,X)}static addMethod(Q,X){C1(this.prototype,Q,X)}static addChainableMethod(Q,X,W){k1(this.prototype,Q,X,W)}static overwriteProperty(Q,X){T1(this.prototype,Q,X)}static overwriteMethod(Q,X){A1(this.prototype,Q,X)}static overwriteChainableMethod(Q,X,W){S1(this.prototype,Q,X,W)}assert(Q,X,W,$,J,G){let U=P1(this,arguments);if(G!==!1)G=!0;if($===void 0&&J===void 0)G=!1;if(e.showDiff!==!0)G=!1;if(!U){X=D1(this,arguments);let Y={actual:H0(this,arguments),expected:$,showDiff:G},K=E1(this,arguments);if(K)Y.operator=K;throw new _(X,Y,e.includeStack?this.assert:k(this,"ssfi"))}}get _obj(){return k(this,"object")}set _obj(Q){k(this,"object",Q)}};R(S7,"Assertion");var q=S7,N0=new EventTarget,j7=class extends Event{constructor(Q,X,W){super(Q);this.name=String(X),this.fn=W}};R(j7,"PluginEvent");var I1=j7;function o9(){return e.useProxy&&typeof Proxy<"u"&&typeof Reflect<"u"}R(o9,"isProxyEnabled");function L1(Z,Q,X){X=X===void 0?function(){}:X,Object.defineProperty(Z,Q,{get:R(function W(){if(!o9()&&!k(this,"lockSsfi"))k(this,"ssfi",W);let $=X.call(this);if($!==void 0)return $;let J=new q;return K9(this,J),J},"propertyGetter"),configurable:!0}),N0.dispatchEvent(new I1("addProperty",Q,X))}R(L1,"addProperty");var o5=Object.getOwnPropertyDescriptor(function(){},"length");function r9(Z,Q,X){if(!o5.configurable)return Z;return Object.defineProperty(Z,"length",{get:R(function(){if(X)throw Error("Invalid Chai property: "+Q+'.length. Due to a compatibility issue, "length" cannot directly follow "'+Q+'". Use "'+Q+'.lengthOf" instead.');throw Error("Invalid Chai property: "+Q+'.length. See docs for proper usage of "'+Q+'".')},"get")}),Z}R(r9,"addLengthGuard");function _7(Z){let Q=Object.getOwnPropertyNames(Z);function X($){if(Q.indexOf($)===-1)Q.push($)}R(X,"addProperty");let W=Object.getPrototypeOf(Z);while(W!==null)Object.getOwnPropertyNames(W).forEach(X),W=Object.getPrototypeOf(W);return Q}R(_7,"getProperties");var l6=["__flags","__methods","_obj","assert"];function y9(Z,Q){if(!o9())return Z;return new Proxy(Z,{get:R(function X(W,$){if(typeof $==="string"&&e.proxyExcludedKeys.indexOf($)===-1&&!Reflect.has(W,$)){if(Q)throw Error("Invalid Chai property: "+Q+"."+$+'. See docs for proper usage of "'+Q+'".');let J=null,G=4;if(_7(W).forEach(function(U){if(!Object.prototype.hasOwnProperty(U)&&l6.indexOf(U)===-1){let z=E7($,U,G);if(z=X)return X;let W=[];for(let $=0;$<=Z.length;$++)W[$]=Array(Q.length+1).fill(0),W[$][0]=$;for(let $=0;$=X){W[$][G]=X;continue}W[$][G]=Math.min(W[$-1][G]+1,W[$][G-1]+1,W[$-1][G-1]+(J===Q.charCodeAt(G-1)?0:1))}}return W[Z.length][Q.length]}R(E7,"stringDistanceCapped");function C1(Z,Q,X){let W=R(function(){if(!k(this,"lockSsfi"))k(this,"ssfi",W);let $=X.apply(this,arguments);if($!==void 0)return $;let J=new q;return K9(this,J),J},"methodWrapper");r9(W,Q,!1),Z[Q]=y9(W,Q),N0.dispatchEvent(new I1("addMethod",Q,X))}R(C1,"addMethod");function T1(Z,Q,X){let W=Object.getOwnPropertyDescriptor(Z,Q),$=R(function(){},"_super");if(W&&typeof W.get==="function")$=W.get;Object.defineProperty(Z,Q,{get:R(function J(){if(!o9()&&!k(this,"lockSsfi"))k(this,"ssfi",J);let G=k(this,"lockSsfi");k(this,"lockSsfi",!0);let U=X($).call(this);if(k(this,"lockSsfi",G),U!==void 0)return U;let z=new q;return K9(this,z),z},"overwritingPropertyGetter"),configurable:!0})}R(T1,"overwriteProperty");function A1(Z,Q,X){let W=Z[Q],$=R(function(){throw Error(Q+" is not a function")},"_super");if(W&&typeof W==="function")$=W;let J=R(function(){if(!k(this,"lockSsfi"))k(this,"ssfi",J);let G=k(this,"lockSsfi");k(this,"lockSsfi",!0);let U=X($).apply(this,arguments);if(k(this,"lockSsfi",G),U!==void 0)return U;let z=new q;return K9(this,z),z},"overwritingMethodWrapper");r9(J,Q,!1),Z[Q]=y9(J,Q)}R(A1,"overwriteMethod");var r5=typeof Object.setPrototypeOf==="function",p6=R(function(){},"testFn"),a5=Object.getOwnPropertyNames(p6).filter(function(Z){let Q=Object.getOwnPropertyDescriptor(p6,Z);if(typeof Q!=="object")return!0;return!Q.configurable}),s5=Function.prototype.call,t5=Function.prototype.apply,x7=class extends I1{constructor(Q,X,W,$){super(Q,X,W);this.chainingBehavior=$}};R(x7,"PluginAddChainableMethodEvent");var e5=x7;function k1(Z,Q,X,W){if(typeof W!=="function")W=R(function(){},"chainingBehavior");let $={method:X,chainingBehavior:W};if(!Z.__methods)Z.__methods={};Z.__methods[Q]=$,Object.defineProperty(Z,Q,{get:R(function(){$.chainingBehavior.call(this);let G=R(function(){if(!k(this,"lockSsfi"))k(this,"ssfi",G);let U=$.method.apply(this,arguments);if(U!==void 0)return U;let z=new q;return K9(this,z),z},"chainableMethodWrapper");if(r9(G,Q,!0),r5){let U=Object.create(this);U.call=s5,U.apply=t5,Object.setPrototypeOf(G,U)}else Object.getOwnPropertyNames(Z).forEach(function(z){if(a5.indexOf(z)!==-1)return;let Y=Object.getOwnPropertyDescriptor(Z,z);Object.defineProperty(G,z,Y)});return K9(this,G),y9(G)},"chainableMethodGetter"),configurable:!0}),N0.dispatchEvent(new e5("addChainableMethod",Q,X,W))}R(k1,"addChainableMethod");function S1(Z,Q,X,W){let $=Z.__methods[Q],J=$.chainingBehavior;$.chainingBehavior=R(function(){let z=W(J).call(this);if(z!==void 0)return z;let Y=new q;return K9(this,Y),Y},"overwritingChainableMethodGetter");let G=$.method;$.method=R(function(){let z=X(G).apply(this,arguments);if(z!==void 0)return z;let Y=new q;return K9(this,Y),Y},"overwritingChainableMethodWrapper")}R(S1,"overwriteChainableMethod");function K0(Z,Q){return j(Z)1&&w===B.length)throw P;return}}this.assert(K,"expected #{this} to "+z+"include "+j(Z),"expected #{this} to not "+z+"include "+j(Z))}R(s9,"include");q.addChainableMethod("include",s9,a9);q.addChainableMethod("contain",s9,a9);q.addChainableMethod("contains",s9,a9);q.addChainableMethod("includes",s9,a9);q.addProperty("ok",function(){this.assert(D(this,"object"),"expected #{this} to be truthy","expected #{this} to be falsy")});q.addProperty("true",function(){this.assert(D(this,"object")===!0,"expected #{this} to be true","expected #{this} to be false",D(this,"negate")?!1:!0)});q.addProperty("numeric",function(){let Z=D(this,"object");this.assert(["Number","BigInt"].includes(h(Z)),"expected #{this} to be numeric","expected #{this} to not be numeric",D(this,"negate")?!1:!0)});q.addProperty("callable",function(){let Z=D(this,"object"),Q=D(this,"ssfi"),X=D(this,"message"),W=X?`${X}: `:"",$=D(this,"negate"),J=$?`${W}expected ${j(Z)} not to be a callable function`:`${W}expected ${j(Z)} to be a callable function`,G=["Function","AsyncFunction","GeneratorFunction","AsyncGeneratorFunction"].includes(h(Z));if(G&&$||!G&&!$)throw new _(J,void 0,Q)});q.addProperty("false",function(){this.assert(D(this,"object")===!1,"expected #{this} to be false","expected #{this} to be true",D(this,"negate")?!0:!1)});q.addProperty("null",function(){this.assert(D(this,"object")===null,"expected #{this} to be null","expected #{this} not to be null")});q.addProperty("undefined",function(){this.assert(D(this,"object")===void 0,"expected #{this} to be undefined","expected #{this} not to be undefined")});q.addProperty("NaN",function(){this.assert(B0(D(this,"object")),"expected #{this} to be NaN","expected #{this} not to be NaN")});function v1(){let Z=D(this,"object");this.assert(Z!==null&&Z!==void 0,"expected #{this} to exist","expected #{this} to not exist")}R(v1,"assertExist");q.addProperty("exist",v1);q.addProperty("exists",v1);q.addProperty("empty",function(){let Z=D(this,"object"),Q=D(this,"ssfi"),X=D(this,"message"),W;switch(X=X?X+": ":"",h(Z).toLowerCase()){case"array":case"string":W=Z.length;break;case"map":case"set":W=Z.size;break;case"weakmap":case"weakset":throw new _(X+".empty was passed a weak collection",void 0,Q);case"function":{let $=X+".empty was passed a function "+D0(Z);throw new _($.trim(),void 0,Q)}default:if(Z!==Object(Z))throw new _(X+".empty was passed non-string primitive "+j(Z),void 0,Q);W=Object.keys(Z).length}this.assert(W===0,"expected #{this} to be empty","expected #{this} not to be empty")});function h1(){let Z=D(this,"object"),Q=h(Z);this.assert(Q==="Arguments","expected #{this} to be arguments but got "+Q,"expected #{this} to not be arguments")}R(h1,"checkArguments");q.addProperty("arguments",h1);q.addProperty("Arguments",h1);function F0(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object");if(D(this,"deep")){let W=D(this,"lockSsfi");D(this,"lockSsfi",!0),this.eql(Z),D(this,"lockSsfi",W)}else this.assert(Z===X,"expected #{this} to equal #{exp}","expected #{this} to not equal #{exp}",Z,this._obj,!0)}R(F0,"assertEqual");q.addMethod("equal",F0);q.addMethod("equals",F0);q.addMethod("eq",F0);function y1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"eql");this.assert(X(Z,D(this,"object")),"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",Z,this._obj,!0)}R(y1,"assertEql");q.addMethod("eql",y1);q.addMethod("eqls",y1);function R0(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"doLength"),$=D(this,"message"),J=$?$+": ":"",G=D(this,"ssfi"),U=h(X).toLowerCase(),z=h(Z).toLowerCase();if(W&&U!=="map"&&U!=="set")new q(X,$,G,!0).to.have.property("length");if(!W&&U==="date"&&z!=="date")throw new _(J+"the argument to above must be a date",void 0,G);else if(!l(Z)&&(W||l(X)))throw new _(J+"the argument to above must be a number",void 0,G);else if(!W&&U!=="date"&&!l(X)){let Y=U==="string"?"'"+X+"'":X;throw new _(J+"expected "+Y+" to be a number or a date",void 0,G)}if(W){let Y="length",K;if(U==="map"||U==="set")Y="size",K=X.size;else K=X.length;this.assert(K>Z,"expected #{this} to have a "+Y+" above #{exp} but got #{act}","expected #{this} to not have a "+Y+" above #{exp}",Z,K)}else this.assert(X>Z,"expected #{this} to be above #{exp}","expected #{this} to be at most #{exp}",Z)}R(R0,"assertAbove");q.addMethod("above",R0);q.addMethod("gt",R0);q.addMethod("greaterThan",R0);function M0(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"doLength"),$=D(this,"message"),J=$?$+": ":"",G=D(this,"ssfi"),U=h(X).toLowerCase(),z=h(Z).toLowerCase(),Y,K=!0;if(W&&U!=="map"&&U!=="set")new q(X,$,G,!0).to.have.property("length");if(!W&&U==="date"&&z!=="date")Y=J+"the argument to least must be a date";else if(!l(Z)&&(W||l(X)))Y=J+"the argument to least must be a number";else if(!W&&U!=="date"&&!l(X)){let B=U==="string"?"'"+X+"'":X;Y=J+"expected "+B+" to be a number or a date"}else K=!1;if(K)throw new _(Y,void 0,G);if(W){let B="length",P;if(U==="map"||U==="set")B="size",P=X.size;else P=X.length;this.assert(P>=Z,"expected #{this} to have a "+B+" at least #{exp} but got #{act}","expected #{this} to have a "+B+" below #{exp}",Z,P)}else this.assert(X>=Z,"expected #{this} to be at least #{exp}","expected #{this} to be below #{exp}",Z)}R(M0,"assertLeast");q.addMethod("least",M0);q.addMethod("gte",M0);q.addMethod("greaterThanOrEqual",M0);function O0(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"doLength"),$=D(this,"message"),J=$?$+": ":"",G=D(this,"ssfi"),U=h(X).toLowerCase(),z=h(Z).toLowerCase(),Y,K=!0;if(W&&U!=="map"&&U!=="set")new q(X,$,G,!0).to.have.property("length");if(!W&&U==="date"&&z!=="date")Y=J+"the argument to below must be a date";else if(!l(Z)&&(W||l(X)))Y=J+"the argument to below must be a number";else if(!W&&U!=="date"&&!l(X)){let B=U==="string"?"'"+X+"'":X;Y=J+"expected "+B+" to be a number or a date"}else K=!1;if(K)throw new _(Y,void 0,G);if(W){let B="length",P;if(U==="map"||U==="set")B="size",P=X.size;else P=X.length;this.assert(P=Z&&H<=Q,"expected #{this} to have a "+N+" within "+w,"expected #{this} to not have a "+N+" within "+w)}else this.assert(W>=Z&&W<=Q,"expected #{this} to be within "+w,"expected #{this} to not be within "+w)});function b1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"ssfi"),$=D(this,"message"),J;try{J=X instanceof Z}catch(U){if(U instanceof TypeError)throw $=$?$+": ":"",new _($+"The instanceof assertion needs a constructor but "+h(Z)+" was given.",void 0,W);throw U}let G=D0(Z);if(G==null)G="an unnamed constructor";this.assert(J,"expected #{this} to be an instance of "+G,"expected #{this} to not be an instance of "+G)}R(b1,"assertInstanceOf");q.addMethod("instanceof",b1);q.addMethod("instanceOf",b1);function u1(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"nested"),$=D(this,"own"),J=D(this,"message"),G=D(this,"object"),U=D(this,"ssfi"),z=typeof Z;if(J=J?J+": ":"",W){if(z!=="string")throw new _(J+"the argument to property must be a string when using nested syntax",void 0,U)}else if(z!=="string"&&z!=="number"&&z!=="symbol")throw new _(J+"the argument to property must be a string, number, or symbol",void 0,U);if(W&&$)throw new _(J+'The "nested" and "own" flags cannot be combined.',void 0,U);if(G===null||G===void 0)throw new _(J+"Target cannot be null or undefined.",void 0,U);let Y=D(this,"deep"),K=D(this,"negate"),B=W?O1(G,Z):null,P=W?B.value:G[Z],w=Y?D(this,"eql"):(F,M)=>F===M,N="";if(Y)N+="deep ";if($)N+="own ";if(W)N+="nested ";N+="property ";let H;if($)H=Object.prototype.hasOwnProperty.call(G,Z);else if(W)H=B.exists;else H=P0(G,Z);if(!K||arguments.length===1)this.assert(H,"expected #{this} to have "+N+j(Z),"expected #{this} to not have "+N+j(Z));if(arguments.length>1)this.assert(H&&w(Q,P),"expected #{this} to have "+N+j(Z)+" of #{exp}, but got #{act}","expected #{this} to not have "+N+j(Z)+" of #{act}",Q,P);D(this,"object",P)}R(u1,"assertProperty");q.addMethod("property",u1);function f1(Z,Q,X){D(this,"own",!0),u1.apply(this,arguments)}R(f1,"assertOwnProperty");q.addMethod("ownProperty",f1);q.addMethod("haveOwnProperty",f1);function g1(Z,Q,X){if(typeof Q==="string")X=Q,Q=null;if(X)D(this,"message",X);let W=D(this,"object"),$=Object.getOwnPropertyDescriptor(Object(W),Z),J=D(this,"eql");if($&&Q)this.assert(J(Q,$),"expected the own property descriptor for "+j(Z)+" on #{this} to match "+j(Q)+", got "+j($),"expected the own property descriptor for "+j(Z)+" on #{this} to not match "+j(Q),Q,$,!0);else this.assert($,"expected #{this} to have an own property descriptor for "+j(Z),"expected #{this} to not have an own property descriptor for "+j(Z));D(this,"object",$)}R(g1,"assertOwnPropertyDescriptor");q.addMethod("ownPropertyDescriptor",g1);q.addMethod("haveOwnPropertyDescriptor",g1);function m1(){D(this,"doLength",!0)}R(m1,"assertLengthChain");function d1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=h(X).toLowerCase(),$=D(this,"message"),J=D(this,"ssfi"),G="length",U;switch(W){case"map":case"set":G="size",U=X.size;break;default:new q(X,$,J,!0).to.have.property("length"),U=X.length}this.assert(U==Z,"expected #{this} to have a "+G+" of #{exp} but got #{act}","expected #{this} to not have a "+G+" of #{act}",Z,U)}R(d1,"assertLength");q.addChainableMethod("length",d1,m1);q.addChainableMethod("lengthOf",d1,m1);function c1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object");this.assert(Z.exec(X),"expected #{this} to match "+Z,"expected #{this} not to match "+Z)}R(c1,"assertMatch");q.addMethod("match",c1);q.addMethod("matches",c1);q.addMethod("string",function(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"message"),$=D(this,"ssfi");new q(X,W,$,!0).is.a("string"),this.assert(~X.indexOf(Z),"expected #{this} to contain "+j(Z),"expected #{this} to not contain "+j(Z))});function l1(Z){let Q=D(this,"object"),X=h(Q),W=h(Z),$=D(this,"ssfi"),J=D(this,"deep"),G,U="",z,Y=!0,K=D(this,"message");K=K?K+": ":"";let B=K+"when testing keys against an object or an array you must give a single Array|Object|String argument or multiple String arguments";if(X==="Map"||X==="Set"){if(U=J?"deeply ":"",z=[],Q.forEach(function(M,O){z.push(O)}),W!=="Array")Z=Array.prototype.slice.call(arguments)}else{switch(z=_1(Q),W){case"Array":if(arguments.length>1)throw new _(B,void 0,$);break;case"Object":if(arguments.length>1)throw new _(B,void 0,$);Z=Object.keys(Z);break;default:Z=Array.prototype.slice.call(arguments)}Z=Z.map(function(M){return typeof M==="symbol"?M:String(M)})}if(!Z.length)throw new _(K+"keys required",void 0,$);let P=Z.length,w=D(this,"any"),N=D(this,"all"),H=Z,F=J?D(this,"eql"):(M,O)=>M===O;if(!w&&!N)N=!0;if(w)Y=H.some(function(M){return z.some(function(O){return F(M,O)})});if(N){if(Y=H.every(function(M){return z.some(function(O){return F(M,O)})}),!D(this,"contains"))Y=Y&&Z.length==z.length}if(P>1){Z=Z.map(function(O){return j(O)});let M=Z.pop();if(N)G=Z.join(", ")+", and "+M;if(w)G=Z.join(", ")+", or "+M}else G=j(Z[0]);G=(P>1?"keys ":"key ")+G,G=(D(this,"contains")?"contain ":"have ")+G,this.assert(Y,"expected #{this} to "+U+G,"expected #{this} to not "+U+G,H.slice(0).sort(K0),z.sort(K0),!0)}R(l1,"assertKeys");q.addMethod("keys",l1);q.addMethod("key",l1);function L0(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"object"),$=D(this,"ssfi"),J=D(this,"message"),G=D(this,"negate")||!1;if(new q(W,J,$,!0).is.a("function"),V0(Z)||typeof Z==="string")Q=Z,Z=null;let U,z=!1;try{W()}catch(w){z=!0,U=w}let Y=Z===void 0&&Q===void 0,K=Boolean(Z&&Q),B=!1,P=!1;if(Y||!Y&&!G){let w="an error";if(Z instanceof Error)w="#{exp}";else if(Z)w=X9.getConstructorName(Z);let N=U;if(U instanceof Error)N=U.toString();else if(typeof U==="string")N=U;else if(U&&(typeof U==="object"||typeof U==="function"))try{N=X9.getConstructorName(U)}catch(H){}this.assert(z,"expected #{this} to throw "+w,"expected #{this} to not throw an error but #{act} was thrown",Z&&Z.toString(),N)}if(Z&&U){if(Z instanceof Error){if(X9.compatibleInstance(U,Z)===G)if(K&&G)B=!0;else this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(U&&!G?" but #{act} was thrown":""),Z.toString(),U.toString())}if(X9.compatibleConstructor(U,Z)===G)if(K&&G)B=!0;else this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(U?" but #{act} was thrown":""),Z instanceof Error?Z.toString():Z&&X9.getConstructorName(Z),U instanceof Error?U.toString():U&&X9.getConstructorName(U))}if(U&&Q!==void 0&&Q!==null){let w="including";if(V0(Q))w="matching";if(X9.compatibleMessage(U,Q)===G)if(K&&G)P=!0;else this.assert(G,"expected #{this} to throw error "+w+" #{exp} but got #{act}","expected #{this} to throw error not "+w+" #{exp}",Q,X9.getMessage(U))}if(B&&P)this.assert(G,"expected #{this} to throw #{exp} but #{act} was thrown","expected #{this} to not throw #{exp}"+(U?" but #{act} was thrown":""),Z instanceof Error?Z.toString():Z&&X9.getConstructorName(Z),U instanceof Error?U.toString():U&&X9.getConstructorName(U));D(this,"object",U)}R(L0,"assertThrows");q.addMethod("throw",L0);q.addMethod("throws",L0);q.addMethod("Throw",L0);function p1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"itself"),$=typeof X==="function"&&!W?X.prototype[Z]:X[Z];this.assert(typeof $==="function","expected #{this} to respond to "+j(Z),"expected #{this} to not respond to "+j(Z))}R(p1,"respondTo");q.addMethod("respondTo",p1);q.addMethod("respondsTo",p1);q.addProperty("itself",function(){D(this,"itself",!0)});function i1(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=Z(X);this.assert(W,"expected #{this} to satisfy "+k9(Z),"expected #{this} to not satisfy"+k9(Z),D(this,"negate")?!1:!0,W)}R(i1,"satisfy");q.addMethod("satisfy",i1);q.addMethod("satisfies",i1);function n1(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"object"),$=D(this,"message"),J=D(this,"ssfi");new q(W,$,J,!0).is.numeric;let G="A `delta` value is required for `closeTo`";if(Q==null)throw new _($?`${$}: ${G}`:G,void 0,J);if(new q(Q,$,J,!0).is.numeric,G="A `expected` value is required for `closeTo`",Z==null)throw new _($?`${$}: ${G}`:G,void 0,J);new q(Z,$,J,!0).is.numeric;let U=R((Y)=>Y<0?-Y:Y,"abs"),z=R((Y)=>parseFloat(parseFloat(Y).toPrecision(12)),"strip");this.assert(z(U(W-Z))<=Q,"expected #{this} to be close to "+Z+" +/- "+Q,"expected #{this} not to be close to "+Z+" +/- "+Q)}R(n1,"closeTo");q.addMethod("closeTo",n1);q.addMethod("approximately",n1);function y7(Z,Q,X,W,$){let J=Array.from(Q),G=Array.from(Z);if(!W){if(G.length!==J.length)return!1;J=J.slice()}return G.every(function(U,z){if($)return X?X(U,J[z]):U===J[z];if(!X){let Y=J.indexOf(U);if(Y===-1)return!1;if(!W)J.splice(Y,1);return!0}return J.some(function(Y,K){if(!X(U,Y))return!1;if(!W)J.splice(K,1);return!0})})}R(y7,"isSubsetOf");q.addMethod("members",function(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"message"),$=D(this,"ssfi");new q(X,W,$,!0).to.be.iterable,new q(Z,W,$,!0).to.be.iterable;let J=D(this,"contains"),G=D(this,"ordered"),U,z,Y;if(J)U=G?"an ordered superset":"a superset",z="expected #{this} to be "+U+" of #{exp}",Y="expected #{this} to not be "+U+" of #{exp}";else U=G?"ordered members":"members",z="expected #{this} to have the same "+U+" as #{exp}",Y="expected #{this} to not have the same "+U+" as #{exp}";let K=D(this,"deep")?D(this,"eql"):void 0;this.assert(y7(Z,X,K,J,G),z,Y,Z,X,!0)});q.addProperty("iterable",function(Z){if(Z)D(this,"message",Z);let Q=D(this,"object");this.assert(Q!=null&&Q[Symbol.iterator],"expected #{this} to be an iterable","expected #{this} to not be an iterable",Q)});function b7(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"object"),W=D(this,"message"),$=D(this,"ssfi"),J=D(this,"contains"),G=D(this,"deep"),U=D(this,"eql");if(new q(Z,W,$,!0).to.be.an("array"),J)this.assert(Z.some(function(z){return X.indexOf(z)>-1}),"expected #{this} to contain one of #{exp}","expected #{this} to not contain one of #{exp}",Z,X);else if(G)this.assert(Z.some(function(z){return U(X,z)}),"expected #{this} to deeply equal one of #{exp}","expected #{this} to deeply equal one of #{exp}",Z,X);else this.assert(Z.indexOf(X)>-1,"expected #{this} to be one of #{exp}","expected #{this} to not be one of #{exp}",Z,X)}R(b7,"oneOf");q.addMethod("oneOf",b7);function o1(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"object"),$=D(this,"message"),J=D(this,"ssfi");new q(W,$,J,!0).is.a("function");let G;if(!Q)new q(Z,$,J,!0).is.a("function"),G=Z();else new q(Z,$,J,!0).to.have.property(Q),G=Z[Q];W();let U=Q===void 0||Q===null?Z():Z[Q],z=Q===void 0||Q===null?G:"."+Q;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",U),D(this,"deltaBehavior","change"),D(this,"realDelta",U!==G),this.assert(G!==U,"expected "+z+" to change","expected "+z+" to not change")}R(o1,"assertChanges");q.addMethod("change",o1);q.addMethod("changes",o1);function r1(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"object"),$=D(this,"message"),J=D(this,"ssfi");new q(W,$,J,!0).is.a("function");let G;if(!Q)new q(Z,$,J,!0).is.a("function"),G=Z();else new q(Z,$,J,!0).to.have.property(Q),G=Z[Q];new q(G,$,J,!0).is.a("number"),W();let U=Q===void 0||Q===null?Z():Z[Q],z=Q===void 0||Q===null?G:"."+Q;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",U),D(this,"deltaBehavior","increase"),D(this,"realDelta",U-G),this.assert(U-G>0,"expected "+z+" to increase","expected "+z+" to not increase")}R(r1,"assertIncreases");q.addMethod("increase",r1);q.addMethod("increases",r1);function a1(Z,Q,X){if(X)D(this,"message",X);let W=D(this,"object"),$=D(this,"message"),J=D(this,"ssfi");new q(W,$,J,!0).is.a("function");let G;if(!Q)new q(Z,$,J,!0).is.a("function"),G=Z();else new q(Z,$,J,!0).to.have.property(Q),G=Z[Q];new q(G,$,J,!0).is.a("number"),W();let U=Q===void 0||Q===null?Z():Z[Q],z=Q===void 0||Q===null?G:"."+Q;D(this,"deltaMsgObj",z),D(this,"initialDeltaValue",G),D(this,"finalDeltaValue",U),D(this,"deltaBehavior","decrease"),D(this,"realDelta",G-U),this.assert(U-G<0,"expected "+z+" to decrease","expected "+z+" to not decrease")}R(a1,"assertDecreases");q.addMethod("decrease",a1);q.addMethod("decreases",a1);function u7(Z,Q){if(Q)D(this,"message",Q);let X=D(this,"deltaMsgObj"),W=D(this,"initialDeltaValue"),$=D(this,"finalDeltaValue"),J=D(this,"deltaBehavior"),G=D(this,"realDelta"),U;if(J==="change")U=Math.abs($-W)===Math.abs(Z);else U=G===Math.abs(Z);this.assert(U,"expected "+X+" to "+J+" by "+Z,"expected "+X+" to not "+J+" by "+Z)}R(u7,"assertDelta");q.addMethod("by",u7);q.addProperty("extensible",function(){let Z=D(this,"object"),Q=Z===Object(Z)&&Object.isExtensible(Z);this.assert(Q,"expected #{this} to be extensible","expected #{this} to not be extensible")});q.addProperty("sealed",function(){let Z=D(this,"object"),Q=Z===Object(Z)?Object.isSealed(Z):!0;this.assert(Q,"expected #{this} to be sealed","expected #{this} to not be sealed")});q.addProperty("frozen",function(){let Z=D(this,"object"),Q=Z===Object(Z)?Object.isFrozen(Z):!0;this.assert(Q,"expected #{this} to be frozen","expected #{this} to not be frozen")});q.addProperty("finite",function(Z){let Q=D(this,"object");this.assert(typeof Q==="number"&&isFinite(Q),"expected #{this} to be a finite number","expected #{this} to not be a finite number")});function w0(Z,Q){if(Z===Q)return!0;if(typeof Q!==typeof Z)return!1;if(typeof Z!=="object"||Z===null)return Z===Q;if(!Q)return!1;if(Array.isArray(Z)){if(!Array.isArray(Q))return!1;return Z.every(function(X){return Q.some(function(W){return w0(X,W)})})}if(Z instanceof Date)if(Q instanceof Date)return Z.getTime()===Q.getTime();else return!1;return Object.keys(Z).every(function(X){let W=Z[X],$=Q[X];if(typeof W==="object"&&W!==null&&$!==null)return w0(W,$);if(typeof W==="function")return W($);return $===W})}R(w0,"compareSubset");q.addMethod("containSubset",function(Z){let Q=k(this,"object"),X=e.showDiff;this.assert(w0(Z,Q),"expected #{act} to contain subset #{exp}","expected #{act} to not contain subset #{exp}",Z,Q,X)});function I9(Z,Q){return new q(Z,Q)}R(I9,"expect");I9.fail=function(Z,Q,X,W){if(arguments.length<2)X=Z,Z=void 0;throw X=X||"expect.fail()",new _(X,{actual:Z,expected:Q,operator:W},I9.fail)};var f7={};H1(f7,{Should:()=>QZ,should:()=>ZZ});function s1(){function Z(){if(this instanceof String||this instanceof Number||this instanceof Boolean||typeof Symbol==="function"&&this instanceof Symbol||typeof BigInt==="function"&&this instanceof BigInt)return new q(this.valueOf(),null,Z);return new q(this,null,Z)}R(Z,"shouldGetter");function Q(W){Object.defineProperty(this,"should",{value:W,enumerable:!0,configurable:!0,writable:!0})}R(Q,"shouldSetter"),Object.defineProperty(Object.prototype,"should",{set:Q,get:Z,configurable:!0});let X={};return X.fail=function(W,$,J,G){if(arguments.length<2)J=W,W=void 0;throw J=J||"should.fail()",new _(J,{actual:W,expected:$,operator:G},X.fail)},X.equal=function(W,$,J){new q(W,J).to.equal($)},X.Throw=function(W,$,J,G){new q(W,G).to.Throw($,J)},X.exist=function(W,$){new q(W,$).to.exist},X.not={},X.not.equal=function(W,$,J){new q(W,J).to.not.equal($)},X.not.Throw=function(W,$,J,G){new q(W,G).to.not.Throw($,J)},X.not.exist=function(W,$){new q(W,$).to.not.exist},X.throw=X.Throw,X.not.throw=X.not.Throw,X}R(s1,"loadShould");var ZZ=s1,QZ=s1;function V(Z,Q){new q(null,null,V,!0).assert(Z,Q,"[ negation message unavailable ]")}R(V,"assert");V.fail=function(Z,Q,X,W){if(arguments.length<2)X=Z,Z=void 0;throw X=X||"assert.fail()",new _(X,{actual:Z,expected:Q,operator:W},V.fail)};V.isOk=function(Z,Q){new q(Z,Q,V.isOk,!0).is.ok};V.isNotOk=function(Z,Q){new q(Z,Q,V.isNotOk,!0).is.not.ok};V.equal=function(Z,Q,X){let W=new q(Z,X,V.equal,!0);W.assert(Q==k(W,"object"),"expected #{this} to equal #{exp}","expected #{this} to not equal #{act}",Q,Z,!0)};V.notEqual=function(Z,Q,X){let W=new q(Z,X,V.notEqual,!0);W.assert(Q!=k(W,"object"),"expected #{this} to not equal #{exp}","expected #{this} to equal #{act}",Q,Z,!0)};V.strictEqual=function(Z,Q,X){new q(Z,X,V.strictEqual,!0).to.equal(Q)};V.notStrictEqual=function(Z,Q,X){new q(Z,X,V.notStrictEqual,!0).to.not.equal(Q)};V.deepEqual=V.deepStrictEqual=function(Z,Q,X){new q(Z,X,V.deepEqual,!0).to.eql(Q)};V.notDeepEqual=function(Z,Q,X){new q(Z,X,V.notDeepEqual,!0).to.not.eql(Q)};V.isAbove=function(Z,Q,X){new q(Z,X,V.isAbove,!0).to.be.above(Q)};V.isAtLeast=function(Z,Q,X){new q(Z,X,V.isAtLeast,!0).to.be.least(Q)};V.isBelow=function(Z,Q,X){new q(Z,X,V.isBelow,!0).to.be.below(Q)};V.isAtMost=function(Z,Q,X){new q(Z,X,V.isAtMost,!0).to.be.most(Q)};V.isTrue=function(Z,Q){new q(Z,Q,V.isTrue,!0).is.true};V.isNotTrue=function(Z,Q){new q(Z,Q,V.isNotTrue,!0).to.not.equal(!0)};V.isFalse=function(Z,Q){new q(Z,Q,V.isFalse,!0).is.false};V.isNotFalse=function(Z,Q){new q(Z,Q,V.isNotFalse,!0).to.not.equal(!1)};V.isNull=function(Z,Q){new q(Z,Q,V.isNull,!0).to.equal(null)};V.isNotNull=function(Z,Q){new q(Z,Q,V.isNotNull,!0).to.not.equal(null)};V.isNaN=function(Z,Q){new q(Z,Q,V.isNaN,!0).to.be.NaN};V.isNotNaN=function(Z,Q){new q(Z,Q,V.isNotNaN,!0).not.to.be.NaN};V.exists=function(Z,Q){new q(Z,Q,V.exists,!0).to.exist};V.notExists=function(Z,Q){new q(Z,Q,V.notExists,!0).to.not.exist};V.isUndefined=function(Z,Q){new q(Z,Q,V.isUndefined,!0).to.equal(void 0)};V.isDefined=function(Z,Q){new q(Z,Q,V.isDefined,!0).to.not.equal(void 0)};V.isCallable=function(Z,Q){new q(Z,Q,V.isCallable,!0).is.callable};V.isNotCallable=function(Z,Q){new q(Z,Q,V.isNotCallable,!0).is.not.callable};V.isObject=function(Z,Q){new q(Z,Q,V.isObject,!0).to.be.a("object")};V.isNotObject=function(Z,Q){new q(Z,Q,V.isNotObject,!0).to.not.be.a("object")};V.isArray=function(Z,Q){new q(Z,Q,V.isArray,!0).to.be.an("array")};V.isNotArray=function(Z,Q){new q(Z,Q,V.isNotArray,!0).to.not.be.an("array")};V.isString=function(Z,Q){new q(Z,Q,V.isString,!0).to.be.a("string")};V.isNotString=function(Z,Q){new q(Z,Q,V.isNotString,!0).to.not.be.a("string")};V.isNumber=function(Z,Q){new q(Z,Q,V.isNumber,!0).to.be.a("number")};V.isNotNumber=function(Z,Q){new q(Z,Q,V.isNotNumber,!0).to.not.be.a("number")};V.isNumeric=function(Z,Q){new q(Z,Q,V.isNumeric,!0).is.numeric};V.isNotNumeric=function(Z,Q){new q(Z,Q,V.isNotNumeric,!0).is.not.numeric};V.isFinite=function(Z,Q){new q(Z,Q,V.isFinite,!0).to.be.finite};V.isBoolean=function(Z,Q){new q(Z,Q,V.isBoolean,!0).to.be.a("boolean")};V.isNotBoolean=function(Z,Q){new q(Z,Q,V.isNotBoolean,!0).to.not.be.a("boolean")};V.typeOf=function(Z,Q,X){new q(Z,X,V.typeOf,!0).to.be.a(Q)};V.notTypeOf=function(Z,Q,X){new q(Z,X,V.notTypeOf,!0).to.not.be.a(Q)};V.instanceOf=function(Z,Q,X){new q(Z,X,V.instanceOf,!0).to.be.instanceOf(Q)};V.notInstanceOf=function(Z,Q,X){new q(Z,X,V.notInstanceOf,!0).to.not.be.instanceOf(Q)};V.include=function(Z,Q,X){new q(Z,X,V.include,!0).include(Q)};V.notInclude=function(Z,Q,X){new q(Z,X,V.notInclude,!0).not.include(Q)};V.deepInclude=function(Z,Q,X){new q(Z,X,V.deepInclude,!0).deep.include(Q)};V.notDeepInclude=function(Z,Q,X){new q(Z,X,V.notDeepInclude,!0).not.deep.include(Q)};V.nestedInclude=function(Z,Q,X){new q(Z,X,V.nestedInclude,!0).nested.include(Q)};V.notNestedInclude=function(Z,Q,X){new q(Z,X,V.notNestedInclude,!0).not.nested.include(Q)};V.deepNestedInclude=function(Z,Q,X){new q(Z,X,V.deepNestedInclude,!0).deep.nested.include(Q)};V.notDeepNestedInclude=function(Z,Q,X){new q(Z,X,V.notDeepNestedInclude,!0).not.deep.nested.include(Q)};V.ownInclude=function(Z,Q,X){new q(Z,X,V.ownInclude,!0).own.include(Q)};V.notOwnInclude=function(Z,Q,X){new q(Z,X,V.notOwnInclude,!0).not.own.include(Q)};V.deepOwnInclude=function(Z,Q,X){new q(Z,X,V.deepOwnInclude,!0).deep.own.include(Q)};V.notDeepOwnInclude=function(Z,Q,X){new q(Z,X,V.notDeepOwnInclude,!0).not.deep.own.include(Q)};V.match=function(Z,Q,X){new q(Z,X,V.match,!0).to.match(Q)};V.notMatch=function(Z,Q,X){new q(Z,X,V.notMatch,!0).to.not.match(Q)};V.property=function(Z,Q,X){new q(Z,X,V.property,!0).to.have.property(Q)};V.notProperty=function(Z,Q,X){new q(Z,X,V.notProperty,!0).to.not.have.property(Q)};V.propertyVal=function(Z,Q,X,W){new q(Z,W,V.propertyVal,!0).to.have.property(Q,X)};V.notPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notPropertyVal,!0).to.not.have.property(Q,X)};V.deepPropertyVal=function(Z,Q,X,W){new q(Z,W,V.deepPropertyVal,!0).to.have.deep.property(Q,X)};V.notDeepPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notDeepPropertyVal,!0).to.not.have.deep.property(Q,X)};V.ownProperty=function(Z,Q,X){new q(Z,X,V.ownProperty,!0).to.have.own.property(Q)};V.notOwnProperty=function(Z,Q,X){new q(Z,X,V.notOwnProperty,!0).to.not.have.own.property(Q)};V.ownPropertyVal=function(Z,Q,X,W){new q(Z,W,V.ownPropertyVal,!0).to.have.own.property(Q,X)};V.notOwnPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notOwnPropertyVal,!0).to.not.have.own.property(Q,X)};V.deepOwnPropertyVal=function(Z,Q,X,W){new q(Z,W,V.deepOwnPropertyVal,!0).to.have.deep.own.property(Q,X)};V.notDeepOwnPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notDeepOwnPropertyVal,!0).to.not.have.deep.own.property(Q,X)};V.nestedProperty=function(Z,Q,X){new q(Z,X,V.nestedProperty,!0).to.have.nested.property(Q)};V.notNestedProperty=function(Z,Q,X){new q(Z,X,V.notNestedProperty,!0).to.not.have.nested.property(Q)};V.nestedPropertyVal=function(Z,Q,X,W){new q(Z,W,V.nestedPropertyVal,!0).to.have.nested.property(Q,X)};V.notNestedPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notNestedPropertyVal,!0).to.not.have.nested.property(Q,X)};V.deepNestedPropertyVal=function(Z,Q,X,W){new q(Z,W,V.deepNestedPropertyVal,!0).to.have.deep.nested.property(Q,X)};V.notDeepNestedPropertyVal=function(Z,Q,X,W){new q(Z,W,V.notDeepNestedPropertyVal,!0).to.not.have.deep.nested.property(Q,X)};V.lengthOf=function(Z,Q,X){new q(Z,X,V.lengthOf,!0).to.have.lengthOf(Q)};V.hasAnyKeys=function(Z,Q,X){new q(Z,X,V.hasAnyKeys,!0).to.have.any.keys(Q)};V.hasAllKeys=function(Z,Q,X){new q(Z,X,V.hasAllKeys,!0).to.have.all.keys(Q)};V.containsAllKeys=function(Z,Q,X){new q(Z,X,V.containsAllKeys,!0).to.contain.all.keys(Q)};V.doesNotHaveAnyKeys=function(Z,Q,X){new q(Z,X,V.doesNotHaveAnyKeys,!0).to.not.have.any.keys(Q)};V.doesNotHaveAllKeys=function(Z,Q,X){new q(Z,X,V.doesNotHaveAllKeys,!0).to.not.have.all.keys(Q)};V.hasAnyDeepKeys=function(Z,Q,X){new q(Z,X,V.hasAnyDeepKeys,!0).to.have.any.deep.keys(Q)};V.hasAllDeepKeys=function(Z,Q,X){new q(Z,X,V.hasAllDeepKeys,!0).to.have.all.deep.keys(Q)};V.containsAllDeepKeys=function(Z,Q,X){new q(Z,X,V.containsAllDeepKeys,!0).to.contain.all.deep.keys(Q)};V.doesNotHaveAnyDeepKeys=function(Z,Q,X){new q(Z,X,V.doesNotHaveAnyDeepKeys,!0).to.not.have.any.deep.keys(Q)};V.doesNotHaveAllDeepKeys=function(Z,Q,X){new q(Z,X,V.doesNotHaveAllDeepKeys,!0).to.not.have.all.deep.keys(Q)};V.throws=function(Z,Q,X,W){if(typeof Q==="string"||Q instanceof RegExp)X=Q,Q=null;let $=new q(Z,W,V.throws,!0).to.throw(Q,X);return k($,"object")};V.doesNotThrow=function(Z,Q,X,W){if(typeof Q==="string"||Q instanceof RegExp)X=Q,Q=null;new q(Z,W,V.doesNotThrow,!0).to.not.throw(Q,X)};V.operator=function(Z,Q,X,W){let $;switch(Q){case"==":$=Z==X;break;case"===":$=Z===X;break;case">":$=Z>X;break;case">=":$=Z>=X;break;case"<":$=ZY instanceof DataView,z=U(Z)?Z.byteLength:Z.length;if(z>0){G+=Q.spacingOuter;let Y=X+Q.indent;for(let K=0;K0){U+=Q.spacingOuter;let Y=X+Q.indent;for(let K=0;K{let G=Z.toString();if(G==="ArrayContaining"||G==="ArrayNotContaining"){if(++W>Q.maxDepth)return`[${G}]`;return`${G+C0}[${k0(Z.sample,Q,X,W,$,J)}]`}if(G==="ObjectContaining"||G==="ObjectNotContaining"){if(++W>Q.maxDepth)return`[${G}]`;return`${G+C0}{${j0(Z.sample,Q,X,W,$,J)}}`}if(G==="StringMatching"||G==="StringNotMatching")return G+C0+J(Z.sample,Q,X,W,$);if(G==="StringContaining"||G==="StringNotContaining")return G+C0+J(Z.sample,Q,X,W,$);if(typeof Z.toAsymmetricMatcher!=="function")throw TypeError(`Asymmetric matcher ${Z.constructor.name} does not implement toAsymmetricMatcher()`);return Z.toAsymmetricMatcher()},UZ=(Z)=>Z&&Z.$$typeof===GZ,zZ={serialize:YZ,test:UZ},KZ=" ",Z8=new Set(["DOMStringMap","NamedNodeMap"]),BZ=/^(?:HTML\w*Collection|NodeList)$/;function VZ(Z){return Z8.has(Z)||BZ.test(Z)}var wZ=(Z)=>Z&&Z.constructor&&!!Z.constructor.name&&VZ(Z.constructor.name);function qZ(Z){return Z.constructor.name==="NamedNodeMap"}var HZ=(Z,Q,X,W,$,J)=>{let G=Z.constructor.name;if(++W>Q.maxDepth)return`[${G}]`;return(Q.min?"":G+KZ)+(Z8.has(G)?`{${j0(qZ(Z)?[...Z].reduce((U,z)=>{return U[z.name]=z.value,U},{}):{...Z},Q,X,W,$,J)}}`:`[${k0([...Z],Q,X,W,$,J)}]`)},PZ={serialize:HZ,test:wZ};function Q8(Z){return Z.replaceAll("<","<").replaceAll(">",">")}function X6(Z,Q,X,W,$,J,G){let U=W+X.indent,z=X.colors;return Z.map((Y)=>{let K=Q[Y];if(typeof K==="string"&&K[0]==="_"&&K.startsWith("__vitest_")&&/__vitest_\d+__/.test(K))return"";let B=G(K,X,U,$,J);if(typeof K!=="string"){if(B.includes(` +`))B=X.spacingOuter+U+B+X.spacingOuter+W;B=`{${B}}`}return`${X.spacingInner+W+z.prop.open+Y+z.prop.close}=${z.value.open}${B}${z.value.close}`}).join("")}function _0(Z,Q,X,W,$,J){return Z.map((G)=>Q.spacingOuter+X+(typeof G==="string"?X8(G,Q):J(G,Q,X,W,$))).join("")}function NZ(Z,Q,X,W,$,J){if(Q.printShadowRoot===!1)return"";return[`${Q.spacingOuter+X}#shadow-root`,_0(Z,Q,X+Q.indent,W,$,J)].join("")}function X8(Z,Q){let X=Q.colors.content;return X.open+Q8(Z)+X.close}function DZ(Z,Q){let X=Q.colors.comment;return`${X.open}${X.close}`}function W6(Z,Q,X,W,$){let J=W.colors.tag;return`${J.open}<${Z}${Q&&J.close+Q+W.spacingOuter+$+J.open}${X?`>${J.close}${X}${W.spacingOuter}${$}${J.open}${J.close}`}function $6(Z,Q){let X=Q.colors.tag;return`${X.open}<${Z}${X.close} …${X.open} />${X.close}`}var FZ=1,J6=3,W8=8,$8=11,RZ=/^(?:(?:HTML|SVG)\w*)?Element$/;function MZ(Z){try{return typeof Z.hasAttribute==="function"&&Z.hasAttribute("is")}catch{return!1}}function OZ(Z){let Q=Z.constructor.name,{nodeType:X,tagName:W}=Z,$=typeof W==="string"&&W.includes("-")||MZ(Z);return X===FZ&&(RZ.test(Q)||$)||X===J6&&Q==="Text"||X===W8&&Q==="Comment"||X===$8&&Q==="DocumentFragment"}var J8=(Z)=>Z?.constructor?.name&&OZ(Z);function IZ(Z){return Z.nodeType===J6}function LZ(Z){return Z.nodeType===W8}function T0(Z){return Z.nodeType===$8}function m7(Z,Q){let X=Z.filter((W)=>{if(W.nodeType===J6)return(W.data||"").trim().length>0;return!0});if(Q)X=X.filter(Q);return X}function G8(Z,Q,X,W,$,J,G){if(IZ(Z))return X8(Z.data,Q);if(LZ(Z))return DZ(Z.data,Q);let U=T0(Z)?"DocumentFragment":Z.tagName.toLowerCase();if(++W>Q.maxDepth)return $6(U,Q);let z=Array.prototype.slice.call(Z.childNodes||Z.children),Y=T0(Z)||!Z.shadowRoot?[]:Array.prototype.slice.call(Z.shadowRoot.children),K=G?m7(z,G):z,B=G?m7(Y,G):Y;return W6(U,X6(T0(Z)?[]:Array.from(Z.attributes,(P)=>P.name).sort(),T0(Z)?{}:[...Z.attributes].reduce((P,w)=>{return P[w.name]=w.value,P},{}),Q,X+Q.indent,W,$,J),(B.length>0?NZ(B,Q,X+Q.indent,W,$,J):"")+_0(K,Q,X+Q.indent,W,$,J),Q,X)}var CZ=(Z,Q,X,W,$,J)=>G8(Z,Q,X,W,$,J);function Y8(Z){return{test:J8,serialize:(Q,X,W,$,J,G)=>G8(Q,X,W,$,J,G,Z)}}var TZ={serialize:CZ,test:J8},AZ="@@__IMMUTABLE_ITERABLE__@@",kZ="@@__IMMUTABLE_LIST__@@",SZ="@@__IMMUTABLE_KEYED__@@",jZ="@@__IMMUTABLE_MAP__@@",d7="@@__IMMUTABLE_ORDERED__@@",_Z="@@__IMMUTABLE_RECORD__@@",EZ="@@__IMMUTABLE_SEQ__@@",xZ="@@__IMMUTABLE_SET__@@",vZ="@@__IMMUTABLE_STACK__@@",b9=(Z)=>`Immutable.${Z}`,E0=(Z)=>`[${Z}]`,t9=" ",c7="…";function hZ(Z,Q,X,W,$,J,G){return++W>Q.maxDepth?E0(b9(G)):`${b9(G)+t9}{${S0(Z.entries(),Q,X,W,$,J)}}`}function yZ(Z){let Q=0;return{next(){if(QQ.maxDepth?E0(G):`${G+t9}{${S0(yZ(Z),Q,X,W,$,J)}}`}function uZ(Z,Q,X,W,$,J){let G=b9("Seq");if(++W>Q.maxDepth)return E0(G);if(Z[SZ])return`${G+t9}{${Z._iter||Z._object?S0(Z.entries(),Q,X,W,$,J):c7}}`;return`${G+t9}[${Z._iter||Z._array||Z._collection||Z._iterable?Q6(Z.values(),Q,X,W,$,J):c7}]`}function t1(Z,Q,X,W,$,J,G){return++W>Q.maxDepth?E0(b9(G)):`${b9(G)+t9}[${Q6(Z.values(),Q,X,W,$,J)}]`}var fZ=(Z,Q,X,W,$,J)=>{if(Z[jZ])return hZ(Z,Q,X,W,$,J,Z[d7]?"OrderedMap":"Map");if(Z[kZ])return t1(Z,Q,X,W,$,J,"List");if(Z[xZ])return t1(Z,Q,X,W,$,J,Z[d7]?"OrderedSet":"Set");if(Z[vZ])return t1(Z,Q,X,W,$,J,"Stack");if(Z[EZ])return uZ(Z,Q,X,W,$,J);return bZ(Z,Q,X,W,$,J)},gZ=(Z)=>Z&&(Z[AZ]===!0||Z[_Z]===!0),mZ={serialize:fZ,test:gZ};function U8(Z){return Z&&Z.__esModule&&Object.prototype.hasOwnProperty.call(Z,"default")?Z.default:Z}var e1={exports:{}},y={},l7;function dZ(){if(l7)return y;l7=1;var Z=Symbol.for("react.transitional.element"),Q=Symbol.for("react.portal"),X=Symbol.for("react.fragment"),W=Symbol.for("react.strict_mode"),$=Symbol.for("react.profiler"),J=Symbol.for("react.consumer"),G=Symbol.for("react.context"),U=Symbol.for("react.forward_ref"),z=Symbol.for("react.suspense"),Y=Symbol.for("react.suspense_list"),K=Symbol.for("react.memo"),B=Symbol.for("react.lazy"),P=Symbol.for("react.view_transition"),w=Symbol.for("react.client.reference");function N(H){if(typeof H==="object"&&H!==null){var F=H.$$typeof;switch(F){case Z:switch(H=H.type,H){case X:case $:case W:case z:case Y:case P:return H;default:switch(H=H&&H.$$typeof,H){case G:case U:case B:case K:return H;case J:return H;default:return F}}case Q:return F}}}return y.ContextConsumer=J,y.ContextProvider=G,y.Element=Z,y.ForwardRef=U,y.Fragment=X,y.Lazy=B,y.Memo=K,y.Portal=Q,y.Profiler=$,y.StrictMode=W,y.Suspense=z,y.SuspenseList=Y,y.isContextConsumer=function(H){return N(H)===J},y.isContextProvider=function(H){return N(H)===G},y.isElement=function(H){return typeof H==="object"&&H!==null&&H.$$typeof===Z},y.isForwardRef=function(H){return N(H)===U},y.isFragment=function(H){return N(H)===X},y.isLazy=function(H){return N(H)===B},y.isMemo=function(H){return N(H)===K},y.isPortal=function(H){return N(H)===Q},y.isProfiler=function(H){return N(H)===$},y.isStrictMode=function(H){return N(H)===W},y.isSuspense=function(H){return N(H)===z},y.isSuspenseList=function(H){return N(H)===Y},y.isValidElementType=function(H){return typeof H==="string"||typeof H==="function"||H===X||H===$||H===W||H===z||H===Y||typeof H==="object"&&H!==null&&(H.$$typeof===B||H.$$typeof===K||H.$$typeof===G||H.$$typeof===J||H.$$typeof===U||H.$$typeof===w||H.getModuleId!==void 0)?!0:!1},y.typeOf=N,y}var p7;function cZ(){if(p7)return e1.exports;return p7=1,e1.exports=dZ(),e1.exports}var z8=cZ(),lZ=U8(z8),pZ=e7({__proto__:null,default:lZ},[z8]),Z6={exports:{}},x={},i7;function iZ(){if(i7)return x;i7=1;var Z=Symbol.for("react.element"),Q=Symbol.for("react.portal"),X=Symbol.for("react.fragment"),W=Symbol.for("react.strict_mode"),$=Symbol.for("react.profiler"),J=Symbol.for("react.provider"),G=Symbol.for("react.context"),U=Symbol.for("react.server_context"),z=Symbol.for("react.forward_ref"),Y=Symbol.for("react.suspense"),K=Symbol.for("react.suspense_list"),B=Symbol.for("react.memo"),P=Symbol.for("react.lazy"),w=Symbol.for("react.offscreen"),N;N=Symbol.for("react.module.reference");function H(F){if(typeof F==="object"&&F!==null){var M=F.$$typeof;switch(M){case Z:switch(F=F.type,F){case X:case $:case W:case Y:case K:return F;default:switch(F=F&&F.$$typeof,F){case U:case G:case z:case P:case B:case J:return F;default:return M}}case Q:return M}}}return x.ContextConsumer=G,x.ContextProvider=J,x.Element=Z,x.ForwardRef=z,x.Fragment=X,x.Lazy=P,x.Memo=B,x.Portal=Q,x.Profiler=$,x.StrictMode=W,x.Suspense=Y,x.SuspenseList=K,x.isAsyncMode=function(){return!1},x.isConcurrentMode=function(){return!1},x.isContextConsumer=function(F){return H(F)===G},x.isContextProvider=function(F){return H(F)===J},x.isElement=function(F){return typeof F==="object"&&F!==null&&F.$$typeof===Z},x.isForwardRef=function(F){return H(F)===z},x.isFragment=function(F){return H(F)===X},x.isLazy=function(F){return H(F)===P},x.isMemo=function(F){return H(F)===B},x.isPortal=function(F){return H(F)===Q},x.isProfiler=function(F){return H(F)===$},x.isStrictMode=function(F){return H(F)===W},x.isSuspense=function(F){return H(F)===Y},x.isSuspenseList=function(F){return H(F)===K},x.isValidElementType=function(F){return typeof F==="string"||typeof F==="function"||F===X||F===$||F===W||F===Y||F===K||F===w||typeof F==="object"&&F!==null&&(F.$$typeof===P||F.$$typeof===B||F.$$typeof===J||F.$$typeof===G||F.$$typeof===z||F.$$typeof===N||F.getModuleId!==void 0)?!0:!1},x.typeOf=H,x}var n7;function nZ(){if(n7)return Z6.exports;return n7=1,Z6.exports=iZ(),Z6.exports}var K8=nZ(),oZ=U8(K8),rZ=e7({__proto__:null,default:oZ},[K8]),aZ=["isAsyncMode","isConcurrentMode","isContextConsumer","isContextProvider","isElement","isForwardRef","isFragment","isLazy","isMemo","isPortal","isProfiler","isStrictMode","isSuspense","isSuspenseList","isValidElementType"],j9=Object.fromEntries(aZ.map((Z)=>[Z,(Q)=>rZ[Z](Q)||pZ[Z](Q)]));function B8(Z,Q=[]){if(Array.isArray(Z))for(let X of Z)B8(X,Q);else if(Z!=null&&Z!==!1&&Z!=="")Q.push(Z);return Q}function o7(Z){let Q=Z.type;if(typeof Q==="string")return Q;if(typeof Q==="function")return Q.displayName||Q.name||"Unknown";if(j9.isFragment(Z))return"React.Fragment";if(j9.isSuspense(Z))return"React.Suspense";if(typeof Q==="object"&&Q!==null){if(j9.isContextProvider(Z))return"Context.Provider";if(j9.isContextConsumer(Z))return"Context.Consumer";if(j9.isForwardRef(Z)){if(Q.displayName)return Q.displayName;let X=Q.render.displayName||Q.render.name||"";return X===""?"ForwardRef":`ForwardRef(${X})`}if(j9.isMemo(Z)){let X=Q.displayName||Q.type.displayName||Q.type.name||"";return X===""?"Memo":`Memo(${X})`}}return"UNDEFINED"}function sZ(Z){let{props:Q}=Z;return Object.keys(Q).filter((X)=>X!=="children"&&Q[X]!==void 0).sort()}var tZ=(Z,Q,X,W,$,J)=>++W>Q.maxDepth?$6(o7(Z),Q):W6(o7(Z),X6(sZ(Z),Z.props,Q,X+Q.indent,W,$,J),_0(B8(Z.props.children),Q,X+Q.indent,W,$,J),Q,X),eZ=(Z)=>Z!=null&&j9.isElement(Z),Z4={serialize:tZ,test:eZ},Q4=typeof Symbol==="function"&&Symbol.for?Symbol.for("react.test.json"):245830487;function X4(Z){let{props:Q}=Z;return Q?Object.keys(Q).filter((X)=>Q[X]!==void 0).sort():[]}var W4=(Z,Q,X,W,$,J)=>++W>Q.maxDepth?$6(Z.type,Q):W6(Z.type,Z.props?X6(X4(Z),Z.props,Q,X+Q.indent,W,$,J):"",Z.children?_0(Z.children,Q,X+Q.indent,W,$,J):"",Q,X),$4=(Z)=>Z&&Z.$$typeof===Q4,J4={serialize:W4,test:$4},V8=Object.prototype.toString,G4=Date.prototype.toISOString,Y4=Error.prototype.toString,r7=RegExp.prototype.toString;function A0(Z){return typeof Z.constructor==="function"&&Z.constructor.name||"Object"}function U4(Z){return typeof window<"u"&&Z===window}var z4=/^Symbol\((.*)\)(.*)$/,K4=/\n/g;class G6 extends Error{constructor(Z,Q){super(Z);this.stack=Q,this.name=this.constructor.name}}function B4(Z){return Z==="[object Array]"||Z==="[object ArrayBuffer]"||Z==="[object DataView]"||Z==="[object Float32Array]"||Z==="[object Float64Array]"||Z==="[object Int8Array]"||Z==="[object Int16Array]"||Z==="[object Int32Array]"||Z==="[object Uint8Array]"||Z==="[object Uint8ClampedArray]"||Z==="[object Uint16Array]"||Z==="[object Uint32Array]"}function V4(Z){return Object.is(Z,-0)?"-0":String(Z)}function w4(Z){return String(`${Z}n`)}function a7(Z,Q){if(!Q)return"[Function]";return`[Function ${Z.name||"anonymous"}]`}function s7(Z){return String(Z).replace(z4,"Symbol($1)")}function t7(Z){return`[${Y4.call(Z)}]`}function w8(Z,Q,X,W,$){if(Z===!0||Z===!1)return`${Z}`;if(Z===void 0)return"undefined";if(Z===null)return"null";let J=typeof Z;if(J==="number")return V4(Z);if(J==="bigint")return w4(Z);if(J==="string"){let U=$?"'":'"';if(W){let z=$?/['\\]/g:/["\\]/g;return`${U}${Z.replaceAll(z,"\\$&")}${U}`}return`${U}${Z}${U}`}if(J==="function")return a7(Z,Q);if(J==="symbol")return s7(Z);let G=V8.call(Z);if(G==="[object WeakMap]")return"WeakMap {}";if(G==="[object WeakSet]")return"WeakSet {}";if(G==="[object Function]"||G==="[object GeneratorFunction]")return a7(Z,Q);if(G==="[object Symbol]")return s7(Z);if(G==="[object Date]")return Number.isNaN(+Z)?"Date { NaN }":G4.call(Z);if(G==="[object Error]")return t7(Z);if(G==="[object RegExp]"){if(X)return r7.call(Z).replaceAll(/[$()*+.?[\\\]^{|}]/g,"\\$&");return r7.call(Z)}if(Z instanceof Error)return t7(Z);return null}function q8(Z,Q,X,W,$,J){if($.includes(Z))return"[Circular]";$=[...$],$.push(Z);let G=++W>Q.maxDepth,U=Q.min;if(Q.callToJSON&&!G&&Z.toJSON&&typeof Z.toJSON==="function"&&!J)return C9(Z.toJSON(),Q,X,W,$,!0);let z=V8.call(Z);if(z==="[object Arguments]")return G?"[Arguments]":`${U?"":"Arguments "}[${k0(Z,Q,X,W,$,C9)}]`;if(B4(z))return G?`[${Z.constructor.name}]`:`${!Q.printBasicPrototype&&Z.constructor.name==="Array"?"":`${Z.constructor.name} `}[${k0(Z,Q,X,W,$,C9)}]`;if(z==="[object Map]")return G?"[Map]":`Map {${S0(Z.entries(),Q,X,W,$,C9," => ",Z.size)}}`;if(z==="[object Set]")return G?"[Set]":`Set {${Q6(Z.values(),Q,X,W,$,C9,Z.size)}}`;return G||U4(Z)?`[${A0(Z)}]`:`${!Q.printBasicPrototype&&A0(Z)==="Object"?"":`${A0(Z)} `}{${j0(Z,Q,X,W,$,C9)}}`}var q4={test:(Z)=>Z&&Z instanceof Error,serialize(Z,Q,X,W,$,J){if($.includes(Z))return"[Circular]";$=[...$,Z];let G=++W>Q.maxDepth,{message:U,cause:z,...Y}=Z,K={message:U,...typeof z<"u"?{cause:z}:{},...Z instanceof AggregateError?{errors:Z.errors}:{},...Y},B=Z.name!=="Error"?Z.name:A0(Z);return G?`[${B}]`:`${B} {${j0(K,Q,X,W,$,J,null)}}`}};function H4(Z){return Z.serialize!=null}function H8(Z,Q,X,W,$,J){let G;try{G=H4(Z)?Z.serialize(Q,X,W,$,J,C9):Z.print(Q,(U)=>C9(U,X,W,$,J),(U)=>{let z=W+X.indent;return z+U.replaceAll(K4,` +${z}`)},{edgeSpacing:X.spacingOuter,min:X.min,spacing:X.spacingInner},X.colors)}catch(U){throw new G6(U.message,U.stack)}if(typeof G!=="string")throw TypeError(`pretty-format: Plugin must return type "string" but instead returned "${typeof G}".`);return G}function P8(Z,Q){for(let X of Z)try{if(X.test(Q))return X}catch(W){throw new G6(W.message,W.stack)}return null}function C9(Z,Q,X,W,$,J){let G,U=P8(Q.plugins,Z);if(U!==null)G=H8(U,Z,Q,X,W,$);else{let z=w8(Z,Q.printFunctionName,Q.escapeRegex,Q.escapeString,Q.singleQuote);if(z!==null)G=z;else G=q8(Z,Q,X,W,$,J)}if(Q._outputLengthPerDepth[W]??=0,Q._outputLengthPerDepth[W]+=G.length,Q._outputLengthPerDepth[W]>Q.maxOutputLength)Q.maxDepth=0;return G}var Y6={comment:"gray",content:"reset",prop:"yellow",tag:"cyan",value:"green"},N8=Object.keys(Y6),Z9={callToJSON:!0,compareKeys:void 0,escapeRegex:!1,escapeString:!0,highlight:!1,indent:2,maxDepth:Number.POSITIVE_INFINITY,maxOutputLength:1e6,maxWidth:Number.POSITIVE_INFINITY,min:!1,plugins:[],printBasicPrototype:!0,printFunctionName:!0,printShadowRoot:!0,theme:Y6,singleQuote:!1,quoteKeys:!0,spacingInner:` +`,spacingOuter:` +`};function P4(Z){for(let Q of Object.keys(Z))if(!Object.hasOwn(Z9,Q))throw Error(`pretty-format: Unknown option "${Q}".`);if(Z.min&&Z.indent!==void 0&&Z.indent!==0)throw Error('pretty-format: Options "min" and "indent" cannot be used together.')}function N4(){return N8.reduce((Z,Q)=>{let X=Y6[Q],W=X&&f[X];if(W&&typeof W.close==="string"&&typeof W.open==="string")Z[Q]=W;else throw Error(`pretty-format: Option "theme" has a key "${Q}" whose value "${X}" is undefined in ansi-styles.`);return Z},Object.create(null))}function D4(){return N8.reduce((Z,Q)=>{return Z[Q]={close:"",open:""},Z},Object.create(null))}function F4(Z){return Z?.printFunctionName??Z9.printFunctionName}function R4(Z){return Z?.escapeRegex??Z9.escapeRegex}function M4(Z){return Z?.escapeString??Z9.escapeString}function O4(Z){return{callToJSON:Z?.callToJSON??Z9.callToJSON,colors:Z?.highlight?N4():D4(),compareKeys:typeof Z?.compareKeys==="function"||Z?.compareKeys===null?Z.compareKeys:Z9.compareKeys,escapeRegex:R4(Z),escapeString:M4(Z),indent:Z?.min?"":I4(Z?.indent??Z9.indent),maxDepth:Z?.maxDepth??Z9.maxDepth,maxWidth:Z?.maxWidth??Z9.maxWidth,min:Z?.min??Z9.min,plugins:Z?.plugins??Z9.plugins,printBasicPrototype:Z?.printBasicPrototype??!Z?.min,printFunctionName:F4(Z),printShadowRoot:Z?.printShadowRoot??!0,spacingInner:Z?.spacingInner??(Z?.min?" ":` +`),spacingOuter:Z?.spacingOuter??(Z?.min?"":` +`),singleQuote:Z?.singleQuote??Z9.singleQuote,quoteKeys:Z?.quoteKeys??Z9.quoteKeys,maxOutputLength:Z?.maxOutputLength??Z9.maxOutputLength,_outputLengthPerDepth:[]}}function I4(Z){return Array.from({length:Z+1}).join(" ")}function Q9(Z,Q){if(Q)P4(Q);let X=O4(Q),W=P8(X.plugins,Z);if(W!==null)return H8(W,Z,X,"",0,[]);let $=w8(Z,X.printFunctionName,X.escapeRegex,X.escapeString,X.singleQuote);if($!==null)return $;return q8(Z,X,"",0,[])}var e9={AsymmetricMatcher:zZ,DOMCollection:PZ,DOMElement:TZ,Immutable:mZ,ReactElement:Z4,ReactTestComponent:J4,Error:q4};var{AsymmetricMatcher:D8,DOMCollection:F8,DOMElement:L4,Immutable:R8,ReactElement:M8,ReactTestComponent:O8}=e9,C4=[O8,M8,L4,F8,R8,D8];function o(Z,Q=10,{maxLength:X,filterNode:W,...$}={}){let J=X??1e4,G,U=typeof W==="string"?T4(W):W,z=U?[O8,M8,Y8(U),F8,R8,D8]:C4;try{G=Q9(Z,{maxDepth:Q,escapeString:!1,plugins:z,...$})}catch{G=Q9(Z,{callToJSON:!1,maxDepth:Q,escapeString:!1,plugins:z,...$})}return G.length>=J&&Q>1?o(Z,Math.floor(Math.min(Q,Number.MAX_SAFE_INTEGER)/2),{maxLength:X,filterNode:W,...$}):G}function T4(Z){return(W)=>{if(W.nodeType===8)return!1;if(W.nodeType===1&&W.matches)try{return!W.matches(Z)}catch{return!0}return!0}}function I8(Z,Q){let{truncate:X,multiline:W,...$}=Q??{},J={singleQuote:!0,quoteKeys:!1,min:!0,spacingInner:" ",spacingOuter:" ",printBasicPrototype:!1,compareKeys:null,...W?{min:!1,spacingInner:void 0,spacingOuter:void 0}:{}},G=X??0,U=o(Z,void 0,{...J,...$,maxLength:G||void 0});if(G===0||U.length<=G)return U;let z=Object.prototype.toString.call(Z);if(typeof Z==="string"){let Y=G-1;if(Y>0&&S4(U[Y-1]))Y=Y-1;return`'${U.slice(1,Y)}…'`}if(z==="[object Array]"||z==="[object Object]"||z==="[object Set]"||z==="[object Map]")return A4(Z,G,{...J,...$,maxDepth:1});return o(Z,void 0,{...J,...$,maxDepth:0})}function A4(Z,Q,X){function W(J){return o(Z,void 0,{...X,maxWidth:J})}let $=k4(0,Q,(J)=>W(J).length<=Q);return W($)}function k4(Z,Q,X){while(Z+1="\uD800"&&Z<="\uDBFF"}function H9(Z,Q,X){let W=typeof Z;if(!X.includes(W))throw TypeError(`${Q} value must be ${X.join(" or ")}, received "${W}"`)}function Z0(Z){return Z!=null&&typeof Z==="object"&&!Array.isArray(Z)}function j4(Z){return Z===Object.prototype||Z===Function.prototype||Z===RegExp.prototype}function u9(Z){return Object.prototype.toString.apply(Z).slice(8,-1)}function _4(Z,Q){let X=typeof Q==="function"?Q:(W)=>Q.add(W);Object.getOwnPropertyNames(Z).forEach(X),Object.getOwnPropertySymbols(Z).forEach(X)}function z6(Z){let Q=new Set;if(j4(Z))return[];return _4(Z,Q),Array.from(Q)}var L8={forceWritable:!1};function K6(Z,Q=L8){return U6(Z,new WeakMap,Q)}function U6(Z,Q,X=L8){let W,$;if(Q.has(Z))return Q.get(Z);if(Array.isArray(Z)){$=Array.from({length:W=Z.length}),Q.set(Z,$);while(W--)$[W]=U6(Z[W],Q,X);return $}if(Object.prototype.toString.call(Z)==="[object Object]"){$=Object.create(Object.getPrototypeOf(Z)),Q.set(Z,$);let J=z6(Z);for(let G of J){let U=Object.getOwnPropertyDescriptor(Z,G);if(!U)continue;let z=U6(Z[G],Q,X);if(X.forceWritable)Object.defineProperty($,G,{enumerable:U.enumerable,configurable:!0,writable:!0,value:z});else if("get"in U)Object.defineProperty($,G,{...U,get(){return z}});else Object.defineProperty($,G,{...U,value:z})}return $}return Z}function B6(){}function f9(Z){let Q=Z%10,X=Z%100;if(Q===1&&X!==11)return`${Z}st`;if(Q===2&&X!==12)return`${Z}nd`;if(Q===3&&X!==13)return`${Z}rd`;return`${Z}th`}var r=-1,i=1,c=0;class m{0;1;constructor(Z,Q){this[0]=Z,this[1]=Q}}function E4(Z,Q){if(!Z||!Q||Z.charAt(0)!==Q.charAt(0))return 0;let X=0,W=Math.min(Z.length,Q.length),$=W,J=0;while(X<$){if(Z.substring(J,$)===Q.substring(J,$))X=$,J=X;else W=$;$=Math.floor((W-X)/2+X)}return $}function f8(Z,Q){if(!Z||!Q||Z.charAt(Z.length-1)!==Q.charAt(Q.length-1))return 0;let X=0,W=Math.min(Z.length,Q.length),$=W,J=0;while(X<$){if(Z.substring(Z.length-$,Z.length-J)===Q.substring(Q.length-$,Q.length-J))X=$,J=X;else W=$;$=Math.floor((W-X)/2+X)}return $}function C8(Z,Q){let X=Z.length,W=Q.length;if(X===0||W===0)return 0;if(X>W)Z=Z.substring(X-W);else if(X0?X[W-1]:-1,G=0,U=0,z=0,Y=0,$=null,Q=!0}J++}if(Q)g8(Z);y4(Z),J=1;while(J=w){if(P>=K.length/2||P>=B.length/2)Z.splice(J,0,new m(c,B.substring(0,P))),Z[J-1][1]=K.substring(0,K.length-P),Z[J+1][1]=B.substring(P),J++}else if(w>=K.length/2||w>=B.length/2)Z.splice(J,0,new m(c,K.substring(0,w))),Z[J-1][0]=i,Z[J-1][1]=B.substring(0,B.length-w),Z[J+1][0]=r,Z[J+1][1]=K.substring(w),J++;J++}J++}}var T8=/[^a-z0-9]/i,A8=/\s/,k8=/[\r\n]/,v4=/\n\r?\n$/,h4=/^\r?\n\r?\n/;function y4(Z){let Q=1;while(Q=Y)Y=K,G=X,U=W,z=$}if(Z[Q-1][1]!==G){if(G)Z[Q-1][1]=G;else Z.splice(Q-1,1),Q--;if(Z[Q][1]=U,z)Z[Q+1][1]=z;else Z.splice(Q+1,1),Q--}}Q++}}function g8(Z){Z.push(new m(c,""));let Q=0,X=0,W=0,$="",J="",G;while(Q1){if(X!==0&&W!==0){if(G=E4(J,$),G!==0){if(Q-X-W>0&&Z[Q-X-W-1][0]===c)Z[Q-X-W-1][1]+=J.substring(0,G);else Z.splice(0,0,new m(c,J.substring(0,G))),Q++;J=J.substring(G),$=$.substring(G)}if(G=f8(J,$),G!==0)Z[Q][1]=J.substring(J.length-G)+Z[Q][1],J=J.substring(0,J.length-G),$=$.substring(0,$.length-G)}if(Q-=X+W,Z.splice(Q,X+W),$.length)Z.splice(Q,0,new m(r,$)),Q++;if(J.length)Z.splice(Q,0,new m(i,J)),Q++;Q++}else if(Q!==0&&Z[Q-1][0]===c)Z[Q-1][1]+=Z[Q][1],Z.splice(Q,1);else Q++;W=0,X=0,$="",J="";break}if(Z.at(-1)?.[1]==="")Z.pop();let U=!1;Q=1;while(Q{let O=0;while(w{let O=0;while(w<=N&&H<=F&&M(N,F))N-=1,F-=1,O+=1;return O},$=(w,N,H,F,M,O,A)=>{let I=0,L=-w,T=O[I],C=T;O[I]+=X(T+1,N,F+T-L+1,H,M);let S=w{let I=0,L=w,T=O[I],C=T;O[I]-=W(N,T-1,H,F+T-L-1,M);let S=w{let S=F-N,b=H-N,u=M-F-b,v=-u-(w-1),s=-u+(w-1),n=Q,g=w{let S=M-H,b=H-N,u=M-F-b,v=u-w,s=u+w,n=Q,g=w{let T=F-N,C=M-H,S=H-N,b=M-F,a=b-S,u=S,v=S;if(A[0]=N-1,I[0]=H,a%2===0){let s=(w||a)/2,n=(S+b)/2;for(let g=1;g<=n;g+=1)if(u=$(g,H,M,T,O,A,u),g{if(M-F{U9(w9,M9,$9)},isCommon:(w9,$9)=>A9($9,w9)}}let R9=N,Y9=H;N=F,H=M,F=R9,M=Y9}let{foundSubsequence:C,isCommon:S}=A[O?1:0];z(w,N,H,F,M,S,I,L,T);let{nChangePreceding:b,aEndPreceding:a,bEndPreceding:u,nCommonPreceding:v,aCommonPreceding:s,bCommonPreceding:n,nCommonFollowing:g,aCommonFollowing:d,bCommonFollowing:p,nChangeFollowing:P9,aStartFollowing:t,bStartFollowing:G9}=T;if(N{if(typeof N!=="number")throw TypeError(`${Z}: ${w} typeof ${typeof N} is not a number`);if(!Number.isSafeInteger(N))throw RangeError(`${Z}: ${w} value ${N} is not a safe integer`);if(N<0)throw RangeError(`${Z}: ${w} value ${N} is a negative integer`)},B=(w,N)=>{let H=typeof N;if(H!=="function")throw TypeError(`${Z}: ${w} typeof ${H} is not a function`)};function P(w,N,H,F){K("aLength",w),K("bLength",N),B("isCommon",H),B("foundSubsequence",F);let M=X(0,w,0,N,H);if(M!==0)F(M,0,0);if(w!==M||N!==M){let O=M,A=M,I=W(O,w-1,A,N-1,H),L=w-I,T=N-I,C=M+I;if(w!==C&&N!==C)Y(0,O,L,A,T,!1,[{foundSubsequence:F,isCommon:H}],[Q],[Q],{aCommonFollowing:Q,aCommonPreceding:Q,aEndPreceding:Q,aStartFollowing:Q,bCommonFollowing:Q,bCommonPreceding:Q,bEndPreceding:Q,bStartFollowing:Q,nChangeFollowing:Q,nChangePreceding:Q,nCommonFollowing:Q,nCommonPreceding:Q});if(I!==0)F(I,L,T)}}return v0}var g4=f4(),d8=u4(g4);function m4(Z,Q){return Z.replace(/\s+$/,(X)=>Q(X))}function P6(Z,Q,X,W,$,J){return Z.length!==0?X(`${W} ${m4(Z,$)}`):W!==" "?X(W):Q&&J.length!==0?X(`${W} ${J}`):""}function c8(Z,Q,{aColor:X,aIndicator:W,changeLineTrailingSpaceColor:$,emptyFirstOrLastLinePlaceholder:J}){return P6(Z,Q,X,W,$,J)}function l8(Z,Q,{bColor:X,bIndicator:W,changeLineTrailingSpaceColor:$,emptyFirstOrLastLinePlaceholder:J}){return P6(Z,Q,X,W,$,J)}function p8(Z,Q,{commonColor:X,commonIndicator:W,commonLineTrailingSpaceColor:$,emptyFirstOrLastLinePlaceholder:J}){return P6(Z,Q,X,W,$,J)}function j8(Z,Q,X,W,{patchColor:$}){return $(`@@ -${Z+1},${Q-Z} +${X+1},${W-X} @@`)}function d4(Z,Q){let X=Z.length,W=Q.contextLines,$=W+W,J=X,G=!1,U=0,z=0;while(z!==X){let I=z;while(z!==X&&Z[z][0]===c)z+=1;if(I!==z)if(I===0){if(z>W)J-=z-W,G=!0}else if(z===X){let L=z-I;if(L>W)J-=L-W,G=!0}else{let L=z-I;if(L>$)J-=L-$,U+=1}while(z!==X&&Z[z][0]!==c)z+=1}let Y=U!==0||G;if(U!==0)J+=U+1;else if(G)J+=1;let K=J-1,B=[],P=0;if(Y)B.push("");let w=0,N=0,H=0,F=0,M=(I)=>{let L=B.length;B.push(p8(I,L===0||L===K,Q)),H+=1,F+=1},O=(I)=>{let L=B.length;B.push(c8(I,L===0||L===K,Q)),H+=1},A=(I)=>{let L=B.length;B.push(l8(I,L===0||L===K,Q)),F+=1};z=0;while(z!==X){let I=z;while(z!==X&&Z[z][0]===c)z+=1;if(I!==z)if(I===0){if(z>W)I=z-W,w=I,N=I,H=w,F=N;for(let L=I;L!==z;L+=1)M(Z[L][1])}else if(z===X){let L=z-I>W?I+W:z;for(let T=I;T!==L;T+=1)M(Z[T][1])}else{let L=z-I;if(L>$){let T=I+W;for(let S=I;S!==T;S+=1)M(Z[S][1]);B[P]=j8(w,H,N,F,Q),P=B.length,B.push("");let C=L-$;w=H+C,N=F+C,H=w,F=N;for(let S=z-W;S!==z;S+=1)M(Z[S][1])}else for(let T=I;T!==z;T+=1)M(Z[T][1])}while(z!==X&&Z[z][0]===r)O(Z[z][1]),z+=1;while(z!==X&&Z[z][0]===i)A(Z[z][1]),z+=1}if(Y)B[P]=j8(w,H,N,F,Q);return B.join(` +`)}function c4(Z,Q){return Z.map((X,W,$)=>{let J=X[1],G=W===0||W===$.length-1;switch(X[0]){case r:return c8(J,G,Q);case i:return l8(J,G,Q);default:return p8(J,G,Q)}}).join(` +`)}var V6=(Z)=>Z,i8=5,l4=0;function p4(){return{aAnnotation:"Expected",aColor:f.green,aIndicator:"-",bAnnotation:"Received",bColor:f.red,bIndicator:"+",changeColor:f.inverse,changeLineTrailingSpaceColor:V6,commonColor:f.dim,commonIndicator:" ",commonLineTrailingSpaceColor:V6,compareKeys:void 0,contextLines:i8,emptyFirstOrLastLinePlaceholder:"",expand:!1,includeChangeCounts:!1,omitAnnotationLines:!1,patchColor:f.yellow,printBasicPrototype:!1,truncateThreshold:l4,truncateAnnotation:"... Diff result is truncated",truncateAnnotationColor:V6}}function i4(Z){return Z&&typeof Z==="function"?Z:void 0}function n4(Z){return typeof Z==="number"&&Number.isSafeInteger(Z)&&Z>=0?Z:i8}function _9(Z={}){return{...p4(),...Z,compareKeys:i4(Z.compareKeys),contextLines:n4(Z.contextLines)}}function g9(Z){return Z.length===1&&Z[0].length===0}function o4(Z){let Q=0,X=0;return Z.forEach((W)=>{switch(W[0]){case r:Q+=1;break;case i:X+=1;break}}),{a:Q,b:X}}function r4({aAnnotation:Z,aColor:Q,aIndicator:X,bAnnotation:W,bColor:$,bIndicator:J,includeChangeCounts:G,omitAnnotationLines:U},z){if(U)return"";let Y="",K="";if(G){let w=String(z.a),N=String(z.b),H=W.length-Z.length,F=" ".repeat(Math.max(0,H)),M=" ".repeat(Math.max(0,-H)),O=N.length-w.length,A=" ".repeat(Math.max(0,O)),I=" ".repeat(Math.max(0,-O));Y=`${F} ${X} ${A}${w}`,K=`${M} ${J} ${I}${N}`}let B=`${X} ${Z}${Y}`,P=`${J} ${W}${K}`;return`${Q(B)} +${$(P)} + +`}function N6(Z,Q,X){return r4(X,o4(Z))+(X.expand?c4(Z,X):d4(Z,X))+(Q?X.truncateAnnotationColor(` +${X.truncateAnnotation}`):"")}function f0(Z,Q,X){let W=_9(X),[$,J]=n8(g9(Z)?[]:Z,g9(Q)?[]:Q,W);return N6($,J,W)}function a4(Z,Q,X,W,$){if(g9(Z)&&g9(X))Z=[],X=[];if(g9(Q)&&g9(W))Q=[],W=[];if(Z.length!==X.length||Q.length!==W.length)return f0(Z,Q,$);let[J,G]=n8(X,W,$),U=0,z=0;return J.forEach((Y)=>{switch(Y[0]){case r:Y[1]=Z[U],U+=1;break;case i:Y[1]=Q[z],z+=1;break;default:Y[1]=Q[z],U+=1,z+=1}}),N6(J,G,_9($))}function n8(Z,Q,X){let W=X?.truncateThreshold??!1,$=Math.max(Math.floor(X?.truncateThreshold??0),0),J=W?Math.min(Z.length,$):Z.length,G=W?Math.min(Q.length,$):Q.length,U=J!==Z.length||G!==Q.length,z=(w,N)=>Z[w]===Q[N],Y=[],K=0,B=0;d8(J,G,z,(w,N,H)=>{for(;K!==N;K+=1)Y.push(new m(r,Z[K]));for(;B!==H;B+=1)Y.push(new m(i,Q[B]));for(;w!==0;w-=1,K+=1,B+=1)Y.push(new m(c,Q[B]))});for(;K!==J;K+=1)Y.push(new m(r,Z[K]));for(;B!==G;B+=1)Y.push(new m(i,Q[B]));return[Y,U]}function _8(Z){if(Z===void 0)return"undefined";else if(Z===null)return"null";else if(Array.isArray(Z))return"array";else if(typeof Z==="boolean")return"boolean";else if(typeof Z==="function")return"function";else if(typeof Z==="number")return"number";else if(typeof Z==="string")return"string";else if(typeof Z==="bigint")return"bigint";else if(typeof Z==="object"){if(Z!=null){if(Z.constructor===RegExp)return"regexp";else if(Z.constructor===Map)return"map";else if(Z.constructor===Set)return"set";else if(Z.constructor===Date)return"date"}return"object"}else if(typeof Z==="symbol")return"symbol";throw Error(`value of unknown type: ${Z}`)}function E8(Z){return Z.includes(`\r +`)?`\r +`:` +`}function s4(Z,Q,X){let W=X?.truncateThreshold??!1,$=Math.max(Math.floor(X?.truncateThreshold??0),0),J=Z.length,G=Q.length;if(W){let w=Z.includes(` +`),N=Q.includes(` +`),H=E8(Z),F=E8(Q),M=w?`${Z.split(H,$).join(H)} +`:Z,O=N?`${Q.split(F,$).join(F)} +`:Q;J=M.length,G=O.length}let U=J!==Z.length||G!==Q.length,z=(w,N)=>Z[w]===Q[N],Y=0,K=0,B=[];if(d8(J,G,z,(w,N,H)=>{if(Y!==N)B.push(new m(r,Z.slice(Y,N)));if(K!==H)B.push(new m(i,Q.slice(K,H)));Y=N+w,K=H+w,B.push(new m(c,Q.slice(H,K)))}),Y!==J)B.push(new m(r,Z.slice(Y)));if(K!==G)B.push(new m(i,Q.slice(K)));return[B,U]}function t4(Z,Q,X){return Q.reduce((W,$)=>W+($[0]===c?$[1]:$[0]===Z&&$[1].length!==0?X($[1]):""),"")}class q6{op;line;lines;changeColor;constructor(Z,Q){this.op=Z,this.line=[],this.lines=[],this.changeColor=Q}pushSubstring(Z){this.pushDiff(new m(this.op,Z))}pushLine(){this.lines.push(this.line.length!==1?new m(this.op,t4(this.op,this.line,this.changeColor)):this.line[0][0]===this.op?this.line[0]:new m(this.op,this.line[0][1])),this.line.length=0}isLineEmpty(){return this.line.length===0}pushDiff(Z){this.line.push(Z)}align(Z){let Q=Z[1];if(Q.includes(` +`)){let X=Q.split(` +`),W=X.length-1;X.forEach(($,J)=>{if(J{if(G===0){let U=new m(Q,J);if(this.deleteBuffer.isLineEmpty()&&this.insertBuffer.isLineEmpty())this.flushChangeLines(),this.pushDiffCommonLine(U);else this.pushDiffChangeLines(U),this.flushChangeLines()}else if(G<$)this.pushDiffCommonLine(new m(Q,J));else if(J.length!==0)this.pushDiffChangeLines(new m(Q,J))})}else this.pushDiffChangeLines(Z)}getLines(){return this.flushChangeLines(),this.lines}}function e4(Z,Q){let X=new q6(r,Q),W=new q6(i,Q),$=new o8(X,W);return Z.forEach((J)=>{switch(J[0]){case r:X.align(J);break;case i:W.align(J);break;default:$.align(J)}}),$.getLines()}function ZQ(Z,Q){if(Q){let X=Z.length-1;return Z.some((W,$)=>W[0]===c&&($!==X||W[1]!==` +`))}return Z.some((X)=>X[0]===c)}function QQ(Z,Q,X){if(Z!==Q&&Z.length!==0&&Q.length!==0){let W=Z.includes(` +`)||Q.includes(` +`),[$,J]=r8(W?`${Z} +`:Z,W?`${Q} +`:Q,!0,X);if(ZQ($,W)){let G=_9(X),U=e4($,G.changeColor);return N6(U,J,G)}}return f0(Z.split(` +`),Q.split(` +`),X)}function r8(Z,Q,X,W){let[$,J]=s4(Z,Q,W);if(X)x4($);return[$,J]}function H6(Z,Q){let{commonColor:X}=_9(Q);return X(Z)}var{AsymmetricMatcher:XQ,DOMCollection:WQ,DOMElement:$Q,Immutable:JQ,ReactElement:GQ,ReactTestComponent:YQ}=e9,a8=[YQ,GQ,$Q,WQ,JQ,XQ,e9.Error],b0={maxDepth:20,plugins:a8},s8={callToJSON:!1,maxDepth:8,plugins:a8},Q0=(Z,Q)=>Q;function E9(Z,Q,X,W=Q0){if(Object.is(Z,Q))return"";let $=_8(Z),J=$,G=!1;if($==="object"&&typeof Z.asymmetricMatch==="function"){if(Z.$$typeof!==Symbol.for("jest.asymmetricMatcher"))return;if(typeof Z.getExpectedType!=="function")return;J=Z.getExpectedType(),G=J==="string"}if(J!==_8(Q)){let M=function(I){return I.length<=F?I:`${I.slice(0,F)}...`},{aAnnotation:U,aColor:z,aIndicator:Y,bAnnotation:K,bColor:B,bIndicator:P}=_9(X),w=u0(s8,X),N=Q9(Z,w),H=Q9(Q,w),F=1e5;N=W("expected",M(N)),H=W("actual",M(H));let O=`${z(`${Y} ${U}:`)} +${N}`,A=`${B(`${P} ${K}:`)} +${H}`;return`${O} + +${A}`}if(G)return;switch($){case"string":return f0(Z.split(` +`),Q.split(` +`),X);case"boolean":case"number":return zQ(Z,Q,X,W);case"map":return w6(x8(Z),x8(Q),X,W);case"set":return w6(v8(Z),v8(Q),X,W);default:return w6(Z,Q,X,W)}}function UQ(Z){return(Q,X)=>{return Z[Q]=X,X}}function zQ(Z,Q,X,W=Q0){let $=W("expected",Q9(Z,b0)),J=W("actual",Q9(Q,b0));return $===J?"":f0($.split(` +`),J.split(` +`),X)}function x8(Z){return new Map(Array.from(Z.entries()).sort())}function v8(Z){return new Set(Array.from(Z.values()).sort())}function w6(Z,Q,X,W=Q0){let $,J=!1;try{let U=u0(b0,X);$=h8(Z,Q,U,X,W)}catch{J=!0}let G=H6(m8,X);if($===void 0||$===G){let U=u0(s8,X);if($=h8(Z,Q,U,X,W),$!==G&&!J)$=`${H6(b4,X)} + +${$}`}return $}function t8(Z){return u0(b0,Z)}function u0(Z,Q){let{compareKeys:X,printBasicPrototype:W,maxDepth:$}=_9(Q);return{...Z,compareKeys:X,printBasicPrototype:W,maxDepth:$??Z.maxDepth}}function h8(Z,Q,X,W,$=Q0){let J={...X,indent:0},G=Q9(Z,J),U=Q9(Q,J);if(G===U)return H6(m8,W);else{let z=$("expected",Q9(Z,X)),Y=$("actual",Q9(Q,X));return a4(z.split(` +`),Y.split(` +`),G.split(` +`),U.split(` +`),W)}}var y8=20000;function b8(Z){return u9(Z)==="Object"&&typeof Z.asymmetricMatch==="function"}function h0(Z,Q){let X=u9(Z),W=u9(Q);return X===W&&(X==="Object"||X==="Array")}function g0(Z,Q,X,W){let{aAnnotation:$,bAnnotation:J}=_9(X);if(typeof Q==="string"&&typeof Z==="string"&&Q.length>0&&Z.length>0&&Q.length<=y8&&Z.length<=y8&&Q!==Z){if(Q.includes(` +`)||Z.includes(` +`))return QQ(Q,Z,X);let[P]=r8(Q,Z,!0),w=P.some((M)=>M[0]===c),N=KQ($,J),H=N($)+wQ(u8(P,r,w)),F=N(J)+VQ(u8(P,i,w));return`${H} +${F}`}let G=K6(Q,{forceWritable:!0}),U=K6(Z,{forceWritable:!0}),{replacedExpected:z,replacedActual:Y}=y0(U,G),K=W?UQ(W):Q0;return E9(z,Y,X,K)}function y0(Z,Q,X=new WeakSet,W=new WeakSet){if(Z instanceof Error&&Q instanceof Error&&typeof Z.cause<"u"&&typeof Q.cause>"u")return delete Z.cause,{replacedActual:Z,replacedExpected:Q};if(!h0(Z,Q))return{replacedActual:Z,replacedExpected:Q};if(X.has(Z)||W.has(Q))return{replacedActual:Z,replacedExpected:Q};return X.add(Z),W.add(Q),z6(Q).forEach(($)=>{let J=Q[$],G=Z[$];if(b8(J)){if(J.asymmetricMatch(G))Q[$]=G;else if("sample"in J&&J.sample!==void 0&&h0(G,J.sample)){let U=y0(G,J.sample,X,W);Z[$]=U.replacedActual,Q[$]=U.replacedExpected}}else if(b8(G)){if(G.asymmetricMatch(J))Z[$]=J;else if("sample"in G&&G.sample!==void 0&&h0(G.sample,J)){let U=y0(G.sample,J,X,W);Z[$]=U.replacedActual,Q[$]=U.replacedExpected}}else if(h0(G,J)){let U=y0(G,J,X,W);Z[$]=U.replacedActual,Q[$]=U.replacedExpected}}),{replacedActual:Z,replacedExpected:Q}}function KQ(...Z){let Q=Z.reduce((X,W)=>W.length>X?W.length:X,0);return(X)=>`${X}: ${" ".repeat(Q-X.length)}`}var BQ="·";function e8(Z){return Z.replace(/\s+$/gm,(Q)=>BQ.repeat(Q.length))}function VQ(Z){return f.red(e8(o(Z)))}function wQ(Z){return f.green(e8(o(Z)))}function u8(Z,Q,X){return Z.reduce((W,$)=>W+($[0]===c?$[1]:$[0]===Q?X?f.inverse($[1]):$[1]:""),"")}function D9(Z){return typeof Z==="function"&&"_isMockFunction"in Z&&Z._isMockFunction===!0}var qQ=new Set,D6=new Set,Z5=new Set,HQ=new FinalizationRegistry((Z)=>{Z5.delete(Z)}),Q5=new WeakMap,X5=new WeakMap;function F6(Z={}){let{originalImplementation:Q,restore:X,mockImplementation:W,resetToMockImplementation:$,resetToMockName:J}=Z;if(X)qQ.add(X);let G=AQ(Q),U=kQ(),z=NQ({config:G,state:U,...Z}),Y=(W||Q)?.length??0;if(Object.defineProperty(z,"length",{writable:!0,enumerable:!1,value:Y,configurable:!0}),J)G.mockName=z.name||"vi.fn()";Q5.set(z,G),X5.set(U,z);let K=new WeakRef(z);Z5.add(K),HQ.register(z,K),z._isMockFunction=!0,z.getMockImplementation=()=>{return G.onceMockImplementations[0]||G.mockImplementation};let B=()=>{if(!Z.prototypeMembers?.length)G5(z,G.onceMockImplementations[0]||G.mockImplementation||Q)};if(Object.defineProperty(z,"mock",{configurable:!1,enumerable:!0,writable:!1,value:U}),z.mockImplementation=function(w){return G.mockImplementation=w,B(),z},z.mockImplementationOnce=function(w){return G.onceMockImplementations.push(w),B(),z},z.withImplementation=function(w,N){let{mockImplementation:H,onceMockImplementations:F}=G,M=()=>{G.mockImplementation=H,G.onceMockImplementations=F,B()};G.mockImplementation=w,G.onceMockImplementations=[],B();let O=N();if(typeof O==="object"&&typeof O?.then==="function")return O.then(()=>{return M(),z});else M();return z},z.mockReturnThis=function(){return z.mockImplementation(function(){return this})},z.mockReturnValue=function(w){return z.mockImplementation(function(){if(new.target)m9("mockReturnValue");return w})},z.mockReturnValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)m9("mockReturnValueOnce");return w})},z.mockThrow=function(w){return z.mockImplementation(function(){throw w})},z.mockThrowOnce=function(w){return z.mockImplementationOnce(function(){throw w})},z.mockResolvedValue=function(w){return z.mockImplementation(function(){if(new.target)m9("mockResolvedValue");return Promise.resolve(w)})},z.mockResolvedValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)m9("mockResolvedValueOnce");return Promise.resolve(w)})},z.mockRejectedValue=function(w){return z.mockImplementation(function(){if(new.target)m9("mockRejectedValue");return Promise.reject(w)})},z.mockRejectedValueOnce=function(w){return z.mockImplementationOnce(function(){if(new.target)m9("mockRejectedValueOnce");return Promise.reject(w)})},z.mockClear=function(){return U.calls=[],U.contexts=[],U.instances=[],U.invocationCallOrder=[],U.results=[],U.settledResults=[],D6.delete(z),z},z.mockReset=function(){return z.mockClear(),G.mockImplementation=$?W:void 0,G.mockName=J?z.name||"vi.fn()":"vi.fn()",G.onceMockImplementations=[],B(),z},z.mockRestore=function(){return z.mockReset(),X?.()},z.mockName=function(w){if(typeof w==="string")G.mockName=w;return z},z.getMockName=function(){return G.mockName||"vi.fn()"},Symbol.dispose)z[Symbol.dispose]=()=>z.mockRestore();if(W)z.mockImplementation(W);else B();return z}function W5(Z){if(Z!=null&&D9(Z))return Z;return F6({mockImplementation:Z,resetToMockImplementation:!0})}function $5(Z,Q,X){m0(Z!=null,"The vi.spyOn() function could not find an object to spy upon. The first argument must be defined."),m0(typeof Z==="object"||typeof Z==="function","Vitest cannot spy on a primitive value.");let[W,$]=J5(Z,Q)||[];m0($||Q in Z,`The property "${String(Q)}" is not defined on the ${typeof Z}.`);let J=X||"value",G=!1;if(J==="value"&&$&&$.value==null&&$.get)J="get",G=!0;let U;if($){if(U=$[J],U==null&&J==="value")U=Z[Q]}else if(J!=="value")U=()=>Z[Q];else U=Z[Q];let z=G&&U?U():U,Y=typeof z;if(m0(Y==="function"||J!=="value"&&U==null,`vi.spyOn() can only spy on a function. Received ${Y}.`),D9(z))return z;let K=(w)=>{let{value:N,...H}=$||{configurable:!0,writable:!0};if(J!=="value")delete H.writable;H[J]=w,Object.defineProperty(Z,Q,H)},P=F6({restore:()=>{if(W!==Z)Reflect.deleteProperty(Z,Q);else if($&&!U)Object.defineProperty(Z,Q,$);else K(U)},originalImplementation:z,resetToMockName:!0});try{K(G?()=>P:P)}catch(w){if(w instanceof TypeError&&Symbol.toStringTag&&Z[Symbol.toStringTag]==="Module"&&(w.message.includes("Cannot redefine property")||w.message.includes("Cannot replace module namespace")||w.message.includes("can't redefine non-configurable property")))throw TypeError(`Cannot spy on export "${String(Q)}". Module namespace is not configurable in ESM. See: https://vitest.dev/guide/mocking/modules#mocking-a-module`,{cause:w});throw w}return P}function J5(Z,Q){let X=Object.getOwnPropertyDescriptor(Z,Q);if(X)return[Z,X];let W=Object.getPrototypeOf(Z);while(W!==null){let $=Object.getOwnPropertyDescriptor(W,Q);if($)return[W,$];W=Object.getPrototypeOf(W)}}function m0(Z,Q){if(!Z)throw Error(Q)}var PQ=1;function NQ({state:Z,config:Q,name:X,prototypeState:W,prototypeConfig:$,keepMembersImplementation:J,mockImplementation:G,prototypeMembers:U=[]}){let z=Q.mockOriginal,Y=G,K=X||z?.name||"Mock",B=function(){},P=W&&X5.get(W),w={[K]:function(...F){if(D6.add(w[K]),P)D6.add(P);DQ(F,Z,W),FQ(PQ++,Z,W);let M={type:"incomplete",value:void 0},O={type:"incomplete",value:void 0};RQ(M,Z,W),MQ(O,Z,W);let A=new.target?void 0:this,[I,L]=OQ(A,Z,W),[T,C]=IQ(A,Z,W),S=Q.onceMockImplementations.shift()||Q.mockImplementation||$?.onceMockImplementations.shift()||$?.mockImplementation||z||B,b,a,u=!1;try{if(new.target){if(U.length===0)G5(N,S===B?void 0:S);b=Reflect.construct(S,F,new.target);for(let v of U){let s=b[v];if(s!==N.prototype[v])continue;let n=D9(s),g=n?s.mock:void 0,d=n?Q5.get(s):void 0;b[v]=F6({originalImplementation:J?d?.mockOriginal:void 0,prototypeState:g,prototypeConfig:d,keepMembersImplementation:J})}}else b=S.apply(this,F)}catch(v){if(a=v,u=!0,v instanceof TypeError&&v.message.includes("is not a constructor"))console.warn(`[vitest] The ${w[K].getMockName()} mock did not use 'function' or 'class' in its implementation, see https://vitest.dev/api/vi#vi-spyon for examples.`);throw v}finally{if(u)M.type="throw",M.value=a,O.type="rejected",O.value=a;else{if(M.type="return",M.value=b,new.target){if(Z.contexts[T-1]=b,Z.instances[I-1]=b,C!=null&&W)W.contexts[C-1]=b;if(L!=null&&W)W.instances[L-1]=b}if(b instanceof Promise)b.then((v)=>{O.type="fulfilled",O.value=v},(v)=>{O.type="rejected",O.value=v});else O.type="fulfilled",O.value=b}}return b}},N=w[K],H=z||Y;if(H)LQ(N,H);return N}function G5(Z,Q){let X=Z.prototype;if(X==null)return;let W=Q?.prototype??Object.prototype;if(X!==W&&Object.getPrototypeOf(X)!==W)Object.setPrototypeOf(X,W)}function DQ(Z,Q,X){Q.calls.push(Z),X?.calls.push(Z)}function FQ(Z,Q,X){Q.invocationCallOrder.push(Z),X?.invocationCallOrder.push(Z)}function RQ(Z,Q,X){Q.results.push(Z),X?.results.push(Z)}function MQ(Z,Q,X){Q.settledResults.push(Z),X?.settledResults.push(Z)}function OQ(Z,Q,X){let W=Q.instances.push(Z),$=X?.instances.push(Z);return[W,$]}function IQ(Z,Q,X){let W=Q.contexts.push(Z),$=X?.contexts.push(Z);return[W,$]}function LQ(Z,Q){let{properties:X,descriptors:W}=TQ(Q);for(let $ of X){let J=W[$];if(J5(Z,$))continue;Object.defineProperty(Z,$,J)}}var CQ=new Set(["length","name","prototype",Symbol.for("nodejs.util.promisify.custom")]);function TQ(Z){let Q=new Set,X={};while(Z&&Z!==Object.prototype&&Z!==Function.prototype){let W=[...Object.getOwnPropertyNames(Z),...Object.getOwnPropertySymbols(Z)];for(let $ of W){if(X[$]||CQ.has($))continue;Q.add($),X[$]=Object.getOwnPropertyDescriptor(Z,$)}Z=Object.getPrototypeOf(Z)}return{properties:Q,descriptors:X}}function AQ(Z){return{mockImplementation:void 0,mockOriginal:Z,mockName:"vi.fn()",onceMockImplementations:[]}}function kQ(){let Z={calls:[],contexts:[],instances:[],invocationCallOrder:[],settledResults:[],results:[],get lastCall(){return Z.calls.at(-1)}};return Z}function m9(Z){throw TypeError(`Cannot use \`${Z}\` when called with \`new\`. Use \`mockImplementation\` with a \`class\` keyword instead. See https://vitest.dev/api/mock#class-support for more information.`)}function SQ(Z){return Z&&(Z["@@__IMMUTABLE_ITERABLE__@@"]||Z["@@__IMMUTABLE_RECORD__@@"])}var jQ=Object.getPrototypeOf({});function Y5(Z){if(Z instanceof Error)return`: ${Z.message}`;if(typeof Z==="string")return`: ${Z}`;return""}function F9(Z,Q=new WeakMap){if(!Z||typeof Z==="string")return Z;if(Z instanceof Error&&"toJSON"in Z&&typeof Z.toJSON==="function"){let X=Z.toJSON();if(X&&X!==Z&&typeof X==="object"){if(typeof Z.message==="string")X0(()=>X.message??=U5(Z.message));if(typeof Z.stack==="string")X0(()=>X.stack??=Z.stack);if(typeof Z.name==="string")X0(()=>X.name??=Z.name);if(Z.cause!=null)X0(()=>X.cause??=F9(Z.cause,Q))}return F9(X,Q)}if(typeof Z==="function")return`Function<${Z.name||"anonymous"}>`;if(typeof Z==="symbol")return Z.toString();if(typeof Z!=="object")return Z;if(typeof Buffer<"u"&&Z instanceof Buffer)return``;if(typeof Uint8Array<"u"&&Z instanceof Uint8Array)return``;if(SQ(Z))return F9(Z.toJSON(),Q);if(Z instanceof Promise||Z.constructor&&Z.constructor.prototype==="AsyncFunction")return"Promise";if(typeof Element<"u"&&Z instanceof Element)return Z.tagName;if(typeof Z.toJSON==="function")return F9(Z.toJSON(),Q);if(Q.has(Z))return Q.get(Z);if(Array.isArray(Z)){let X=Array(Z.length);return Q.set(Z,X),Z.forEach((W,$)=>{try{X[$]=F9(W,Q)}catch(J){X[$]=Y5(J)}}),X}else{let X=Object.create(null);Q.set(Z,X);let W=Z;while(W&&W!==jQ)Object.getOwnPropertyNames(W).forEach(($)=>{if($ in X)return;try{X[$]=F9(Z[$],Q)}catch(J){delete X[$],X[$]=Y5(J)}}),W=Object.getPrototypeOf(W);if(Z instanceof Error)X0(()=>X.message=U5(Z.message));return X}}function X0(Z){try{return Z()}catch{}}function U5(Z){return Z.replace(/\(0\s?,\s?__vite_ssr_import_\d+__.(\w+)\)/g,"$1").replace(/__(vite_ssr_import|vi_import)_\d+__\./g,"").replace(/getByTestId('__vitest_\d+__')/g,"page")}function R6(Z,Q,X=new WeakSet){if(!Z||typeof Z!=="object")return{message:String(Z)};let W=Z;if(W.showDiff||W.showDiff===void 0&&W.expected!==void 0&&W.actual!==void 0){let $={...Q,...W.diffOptions};W.diff=g0(W.actual,W.expected,$,W),W.expected=z5(W.expected,$),W.actual=z5(W.actual,$)}try{if(!X.has(W)&&typeof W.cause==="object")X.add(W),W.cause=R6(W.cause,Q,X)}catch{}try{return F9(W)}catch($){return F9(Error(`Failed to fully serialize error: ${$?.message} +Inner error message: ${W?.message}`))}}function z5(Z,Q){if(typeof Z!=="string")return Q9(Z,t8(Q));return Z}var d0=Symbol.for("matchers-object"),l0=Symbol.for("$$jest-matchers-object"),D5=Symbol.for("expect-global"),p0=Symbol.for("asymmetric-matchers-object");var c0=f.green,E6=f.red,_Q=f.inverse,EQ=f.bold,T9=f.dim;function xQ(Z,Q="received",X="expected",W={}){let{comment:$="",isDirectExpectCall:J=!1,isNot:G=!1,promise:U="",secondArgument:z="",expectedColor:Y=c0,receivedColor:K=E6,secondArgumentColor:B=c0}=W,P="",w="expect";if(!J&&Q!=="")P+=T9(`${w}(`)+K(Q),w=")";if(U!=="")P+=T9(`${w}.`)+U,w="";if(G)P+=`${T9(`${w}.`)}not`,w="";if(Z.includes("."))w+=Z;else P+=T9(`${w}.`)+Z,w="";if(X==="")w+="()";else{if(P+=T9(`${w}(`)+Y(X),z)P+=T9(", ")+B(z);w=")"}if($!=="")w+=` // ${$}`;if(w!=="")P+=T9(w);return P}var vQ="·";function F5(Z){return Z.replace(/\s+$/gm,(Q)=>vQ.repeat(Q.length))}function hQ(Z){return E6(F5(o(Z)))}function yQ(Z){return c0(F5(o(Z)))}function R5(){return{EXPECTED_COLOR:c0,RECEIVED_COLOR:E6,INVERTED_COLOR:_Q,BOLD_WEIGHT:EQ,DIM_COLOR:T9,diff:E9,matcherHint:xQ,printReceived:hQ,printExpected:yQ,printDiffOrStringify:g0,printWithType:bQ}}function bQ(Z,Q,X){let W=u9(Q),$=W!=="null"&&W!=="undefined"?`${Z} has type: ${W} +`:"",J=`${Z} has value: ${X(Q)}`;return $+J}function x6(){return globalThis[l0].customEqualityTesters}function E(Z,Q,X,W){return X=X||[],W0(Z,Q,[],[],X,W?M5:gQ)}var kX=Function.prototype.toString;function K5(Z){return!!Z&&typeof Z==="object"&&"asymmetricMatch"in Z&&B9("Function",Z.asymmetricMatch)}function uQ(Z,Q,X){let W=K5(Z),$=K5(Q);if(W&&$)return;if(W)return Z.asymmetricMatch(Q,X);if($)return Q.asymmetricMatch(Z,X)}function d9(Z){if(typeof Error.isError==="function")return Error.isError(Z);switch(Object.prototype.toString.call(Z)){case"[object Error]":case"[object Exception]":case"[object DOMException]":return!0;default:return Z instanceof Error}}function W0(Z,Q,X,W,$,J){let G=!0,U=uQ(Z,Q,$);if(U!==void 0)return U;let z={equals:E};for(let N=0;N<$.length;N++){let H=$[N].call(z,Z,Q,$);if(H!==void 0)return H}if(typeof URL==="function"&&Z instanceof URL&&Q instanceof URL)return Z.href===Q.href;if(Object.is(Z,Q))return!0;if(Z===null||Q===null)return Z===Q;let Y=Object.prototype.toString.call(Z);if(Y!==Object.prototype.toString.call(Q))return!1;switch(Y){case"[object Boolean]":case"[object String]":case"[object Number]":if(typeof Z!==typeof Q)return!1;else if(typeof Z!=="object"&&typeof Q!=="object")return Object.is(Z,Q);else return Object.is(Z.valueOf(),Q.valueOf());case"[object Date]":{let N=+Z,H=+Q;return N===H||Number.isNaN(N)&&Number.isNaN(H)}case"[object RegExp]":return Z.source===Q.source&&Z.flags===Q.flags;case"[object Temporal.Instant]":case"[object Temporal.ZonedDateTime]":case"[object Temporal.PlainDateTime]":case"[object Temporal.PlainDate]":case"[object Temporal.PlainTime]":case"[object Temporal.PlainYearMonth]":case"[object Temporal.PlainMonthDay]":return Z.equals(Q);case"[object Temporal.Duration]":return Z.toString()===Q.toString()}if(typeof Z!=="object"||typeof Q!=="object")return!1;if(V5(Z)&&V5(Q))return Z.isEqualNode(Q);let K=X.length;while(K--)if(X[K]===Z)return W[K]===Q;else if(W[K]===Q)return!1;if(X.push(Z),W.push(Q),Y==="[object Array]"&&Z.length!==Q.length)return!1;if(d9(Z)&&d9(Q))try{return fQ(Z,Q,X,W,$,J)}finally{X.pop(),W.pop()}let B=B5(Z,J),P,w=B.length;if(B5(Q,J).length!==w)return!1;while(w--)if(P=B[w],G=J(Q,P)&&W0(Z[P],Q[P],X,W,$,J),!G)return!1;return X.pop(),W.pop(),G}function fQ(Z,Q,X,W,$,J){let G=Object.prototype.toString.call(Z)===Object.prototype.toString.call(Q)&&Z.name===Q.name&&Z.message===Q.message;if(typeof Q.cause<"u")G&&=W0(Z.cause,Q.cause,X,W,$,J);if(Z instanceof AggregateError&&Q instanceof AggregateError)G&&=W0(Z.errors,Q.errors,X,W,$,J);return G&&=W0({...Z},{...Q},X,W,$,J),G}function B5(Z,Q){let X=[];for(let W in Z)if(Q(Z,W))X.push(W);return X.concat(Object.getOwnPropertySymbols(Z).filter((W)=>Object.getOwnPropertyDescriptor(Z,W).enumerable))}function gQ(Z,Q){return M5(Z,Q)&&Z[Q]!==void 0}function M5(Z,Q){return Object.hasOwn(Z,Q)}function B9(Z,Q){return Object.prototype.toString.apply(Q)===`[object ${Z}]`}function V5(Z){return Z!==null&&typeof Z==="object"&&"nodeType"in Z&&typeof Z.nodeType==="number"&&"nodeName"in Z&&typeof Z.nodeName==="string"&&"isEqualNode"in Z&&typeof Z.isEqualNode==="function"}var O5="@@__IMMUTABLE_KEYED__@@",I5="@@__IMMUTABLE_SET__@@",mQ="@@__IMMUTABLE_LIST__@@",i0="@@__IMMUTABLE_ORDERED__@@",dQ="@@__IMMUTABLE_RECORD__@@";function cQ(Z){return!!(Z&&Z[O5]&&!Z[i0])}function lQ(Z){return!!(Z&&Z[I5]&&!Z[i0])}function n0(Z){return Z!=null&&typeof Z==="object"&&!Array.isArray(Z)}function pQ(Z){return Boolean(Z&&n0(Z)&&Z[mQ])}function iQ(Z){return Boolean(Z&&n0(Z)&&Z[O5]&&Z[i0])}function nQ(Z){return Boolean(Z&&n0(Z)&&Z[I5]&&Z[i0])}function oQ(Z){return Boolean(Z&&n0(Z)&&Z[dQ])}var L5=Symbol.iterator;function w5(Z){return!!(Z!=null&&Z[L5])}function W9(Z,Q,X=[],W=[],$=[]){if(typeof Z!=="object"||typeof Q!=="object"||Array.isArray(Z)||Array.isArray(Q)||!w5(Z)||!w5(Q))return;if(Z.constructor!==Q.constructor)return!1;let J=W.length;while(J--)if(W[J]===Z)return $[J]===Q;W.push(Z),$.push(Q);let G=[...X.filter((Y)=>Y!==W9),U];function U(Y,K){return W9(Y,K,[...X],[...W],[...$])}if(Z.size!==void 0){if(Z.size!==Q.size)return!1;else if(B9("Set",Z)||lQ(Z)){let Y=!0;for(let K of Z)if(!Q.has(K)){let B=!1;for(let P of Q)if(E(K,P,G)===!0)B=!0;if(B===!1){Y=!1;break}}return W.pop(),$.pop(),Y}else if(B9("Map",Z)||cQ(Z)){let Y=!0;for(let K of Z)if(!Q.has(K[0])||!E(K[1],Q.get(K[0]),G)){let B=!1;for(let P of Q){let w=E(K[0],P[0],G),N=!1;if(w===!0)N=E(K[1],P[1],G);if(N===!0)B=!0}if(B===!1){Y=!1;break}}return W.pop(),$.pop(),Y}}let z=Q[L5]();for(let Y of Z){let K=z.next();if(K.done||!E(Y,K.value,G))return!1}if(!z.next().done)return!1;if(!pQ(Z)&&!iQ(Z)&&!nQ(Z)&&!oQ(Z)){let Y=Object.entries(Z),K=Object.entries(Q);if(!E(Y,K,G))return!1}return W.pop(),$.pop(),!0}function v6(Z,Q){if(!Z||typeof Z!=="object"||Z===Object.prototype)return!1;return Object.hasOwn(Z,Q)||v6(Object.getPrototypeOf(Z),Q)}function rQ(Z){return Z0(Z)&&!d9(Z)&&!Array.isArray(Z)&&!(Z instanceof Date)&&!(Z instanceof Set)&&!(Z instanceof Map)}function $0(Z,Q,X=[]){let W=X.filter((J)=>J!==$0),$=(J=new WeakMap)=>(G,U)=>{if(!rQ(U))return;return Object.keys(U).every((z)=>{if(U[z]!=null&&typeof U[z]==="object"){if(J.has(U[z]))return E(G[z],U[z],W);J.set(U[z],!0)}let Y=G!=null&&v6(G,z)&&E(G[z],U[z],[...W,$(J)]);return J.delete(U[z]),Y})};return $()(Z,Q)}function q5(Z,Q){if(Z==null||Q==null||Z.constructor===Q.constructor)return;return!1}function H5(Z,Q){let X=Z,W=Q;if(!(Z instanceof DataView&&Q instanceof DataView)){if(!(Z instanceof ArrayBuffer)||!(Q instanceof ArrayBuffer))return;try{X=new DataView(Z),W=new DataView(Q)}catch{return}}if(X.byteLength!==W.byteLength)return!1;for(let $=0;$G!==I6);return E(Z,Q,J,!0)&&E(W,$)}function aQ(Z,Q="#{this}",X="#{exp}"){let W=`expected ${Q} to be ${X} // Object.is equality`;if(["toStrictEqual","toEqual"].includes(Z))return`${W} + +If it should pass with deep equality, replace "toBe" with "${Z}" + +Expected: ${Q} +Received: serializes to the same string +`;return W}function sQ(Z,Q){return`${Q} ${Z}${Q===1?"":"s"}`}function M6(Z){return[...Object.keys(Z),...Object.getOwnPropertySymbols(Z).filter((Q)=>Object.getOwnPropertyDescriptor(Z,Q)?.enumerable)]}function tQ(Z,Q,X){let W=0,$=(J=new WeakMap)=>(G,U)=>{if(Array.isArray(G)){if(Array.isArray(U)&&U.length===G.length)return U.map((z,Y)=>$(J)(G[Y],z))}else if(G instanceof Date)return G;else if(Z0(G)&&Z0(U)){if(E(G,U,[...X,W9,$0]))return U;let z={};if(J.set(G,z),typeof G.constructor==="function"&&typeof G.constructor.name==="string")Object.defineProperty(z,"constructor",{enumerable:!1,value:G.constructor});for(let Y of M6(G))if(v6(U,Y))z[Y]=J.has(G[Y])?J.get(G[Y]):$(J)(G[Y],U[Y]);else if(!J.has(G[Y])){if(W+=1,Z0(G[Y]))W+=M6(G[Y]).length;$(J)(G[Y],U[Y])}if(M6(z).length>0)return z}return G};return{subset:$()(Z,Q),stripped:W}}function eQ(Z){return!!Z&&(typeof Z==="object"||typeof Z==="function")&&Z["~standard"]&&typeof Z["~standard"].validate==="function"}if(!Object.hasOwn(globalThis,d0)){let Z=new WeakMap,Q=Object.create(null),X=[],W=Object.create(null);Object.defineProperty(globalThis,d0,{configurable:!0,get:()=>Z}),Object.defineProperty(globalThis,l0,{configurable:!0,get:()=>({state:Z.get(globalThis[D5]),matchers:Q,customEqualityTesters:X})}),Object.defineProperty(globalThis,p0,{configurable:!0,get:()=>W})}function J0(Z){return globalThis[d0].get(Z)}function o0(Z,Q){let X=globalThis[d0],W=X.get(Q),$=Object.defineProperties(W||{},Object.getOwnPropertyDescriptors(Z));if(!W)X.set(Q,$)}class V9{sample;inverse;$$typeof=Symbol.for("jest.asymmetricMatcher");constructor(Z,Q=!1){this.sample=Z,this.inverse=Q}getMatcherContext(Z){return{...J0(Z||globalThis[D5]),equals:E,isNot:this.inverse,customTesters:x6(),utils:{...R5(),diff:E9,stringify:o,iterableEquality:W9,subsetEquality:$0}}}}V9.prototype[Symbol.for("chai/inspect")]=function(Z){let Q=o(this,Z.depth,{min:!0});if(Q.length<=Z.truncate)return Q;return`${this.toString()}{…}`};class L6 extends V9{constructor(Z,Q=!1){if(!B9("String",Z))throw Error("Expected is not a string");super(Z,Q)}asymmetricMatch(Z){let Q=B9("String",Z)&&Z.includes(this.sample);return this.inverse?!Q:Q}toString(){return`String${this.inverse?"Not":""}Containing`}getExpectedType(){return"string"}}class C5 extends V9{asymmetricMatch(Z){return Z!=null}toString(){return"Anything"}toAsymmetricMatcher(){return"Anything"}}class C6 extends V9{constructor(Z,Q=!1){super(Z,Q)}getPrototype(Z){if(Object.getPrototypeOf)return Object.getPrototypeOf(Z);if(Z.constructor.prototype===Z)return null;return Z.constructor.prototype}hasProperty(Z,Q){if(!Z)return!1;if(Object.hasOwn(Z,Q))return!0;return this.hasProperty(this.getPrototype(Z),Q)}getProperties(Z){return[...Object.keys(Z),...Object.getOwnPropertySymbols(Z).filter((Q)=>Object.getOwnPropertyDescriptor(Z,Q)?.enumerable)]}asymmetricMatch(Z,Q){if(typeof this.sample!=="object")throw TypeError(`You must provide an object to ${this.toString()}, not '${typeof this.sample}'.`);let X=!0,W=this.getProperties(this.sample);for(let $ of W){if(!this.hasProperty(Z,$)){X=!1;break}let J=this.sample[$],G=Z[$];if(!E(J,G,Q)){X=!1;break}}return this.inverse?!X:X}toString(){return`Object${this.inverse?"Not":""}Containing`}getExpectedType(){return"object"}}class T6 extends V9{constructor(Z,Q=!1){super(Z,Q)}asymmetricMatch(Z,Q){if(!Array.isArray(this.sample))throw TypeError(`You must provide an array to ${this.toString()}, not '${typeof this.sample}'.`);let X=this.sample.length===0||Array.isArray(Z)&&this.sample.every((W)=>Z.some(($)=>E(W,$,Q)));return this.inverse?!X:X}toString(){return`Array${this.inverse?"Not":""}Containing`}getExpectedType(){return"array"}}class T5 extends V9{constructor(Z){if(typeof Z>"u")throw TypeError("any() expects to be passed a constructor function. Please pass one or use anything() to match any object.");super(Z)}fnNameFor(Z){if(Z.name)return Z.name;let X=Function.prototype.toString.call(Z).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/);return X?X[1]:""}asymmetricMatch(Z){if(this.sample===String)return typeof Z=="string"||Z instanceof String;if(this.sample===Number)return typeof Z=="number"||Z instanceof Number;if(this.sample===Function)return typeof Z=="function"||typeof Z==="function";if(this.sample===Boolean)return typeof Z=="boolean"||Z instanceof Boolean;if(this.sample===BigInt)return typeof Z=="bigint"||Z instanceof BigInt;if(this.sample===Symbol)return typeof Z=="symbol"||Z instanceof Symbol;if(this.sample===Object)return typeof Z=="object";return Z instanceof this.sample}toString(){return"Any"}getExpectedType(){if(this.sample===String)return"string";if(this.sample===Number)return"number";if(this.sample===Function)return"function";if(this.sample===Object)return"object";if(this.sample===Boolean)return"boolean";return this.fnNameFor(this.sample)}toAsymmetricMatcher(){return`Any<${this.fnNameFor(this.sample)}>`}}class A6 extends V9{constructor(Z,Q=!1){if(!B9("String",Z)&&!B9("RegExp",Z))throw Error("Expected is not a String or a RegExp");super(new RegExp(Z),Q)}asymmetricMatch(Z){let Q=B9("String",Z)&&this.sample.test(Z);return this.inverse?!Q:Q}toString(){return`String${this.inverse?"Not":""}Matching`}getExpectedType(){return"string"}}class k6 extends V9{precision;constructor(Z,Q=2,X=!1){if(!B9("Number",Z))throw Error("Expected is not a Number");if(!B9("Number",Q))throw Error("Precision is not a Number");super(Z);this.inverse=X,this.precision=Q}asymmetricMatch(Z){if(!B9("Number",Z))return!1;let Q=!1;if(Z===Number.POSITIVE_INFINITY&&this.sample===Number.POSITIVE_INFINITY)Q=!0;else if(Z===Number.NEGATIVE_INFINITY&&this.sample===Number.NEGATIVE_INFINITY)Q=!0;else Q=Math.abs(this.sample-Z)<10**-this.precision/2;return this.inverse?!Q:Q}toString(){return`Number${this.inverse?"Not":""}CloseTo`}getExpectedType(){return"number"}toAsymmetricMatcher(){return[this.toString(),this.sample,`(${sQ("digit",this.precision)})`].join(" ")}}class S6 extends V9{result;constructor(Z,Q=!1){if(!eQ(Z))throw TypeError("SchemaMatching expected to receive a Standard Schema.");super(Z,Q)}asymmetricMatch(Z){let Q=this.sample["~standard"].validate(Z);if(Q instanceof Promise)throw TypeError("Async schema validation is not supported in asymmetric matchers.");this.result=Q;let X=!this.result.issues||this.result.issues.length===0;return this.inverse?!X:X}toString(){return`Schema${this.inverse?"Not":""}Matching`}getExpectedType(){return"object"}toAsymmetricMatcher(){let{utils:Z}=this.getMatcherContext();if((this.result?.issues||[]).length>0)return`${this.toString()} ${Z.stringify(this.result,void 0,{printBasicPrototype:!1})}`;return this.toString()}}var A5=(Z,Q)=>{Q.addMethod(Z.expect,"anything",()=>new C5),Q.addMethod(Z.expect,"any",(X)=>new T5(X)),Q.addMethod(Z.expect,"stringContaining",(X)=>new L6(X)),Q.addMethod(Z.expect,"objectContaining",(X)=>new C6(X)),Q.addMethod(Z.expect,"arrayContaining",(X)=>new T6(X)),Q.addMethod(Z.expect,"stringMatching",(X)=>new A6(X)),Q.addMethod(Z.expect,"closeTo",(X,W)=>new k6(X,W)),Q.addMethod(Z.expect,"schemaMatching",(X)=>new S6(X)),Z.expect.not={stringContaining:(X)=>new L6(X,!0),objectContaining:(X)=>new C6(X,!0),arrayContaining:(X)=>new T6(X,!0),stringMatching:(X)=>new A6(X,!0),closeTo:(X,W)=>new k6(X,W,!0),schemaMatching:(X)=>new S6(X,!0)}};function P5(Z,Q,X){let W=Z.flag(Q,"soft")?".soft":"",$=Z.flag(Q,"negate")?"not.":"",J=`${Z.flag(Q,"_name")}(${X?"expected":""})`,G=Z.flag(Q,"promise"),U=G?`.${G}`:"";return`expect${W}(actual)${U}.${$}${J}`}function N5(Z,Q,X,W,$){let J=Z;if(J&&Q instanceof Promise){if(Q=Q.finally(()=>{if(!J.promises)return;let U=J.promises.indexOf(Q);if(U!==-1)J.promises.splice(U,1)}),!J.promises)J.promises=[];if($)Q=Q.then(B6,(U)=>{j6(J,U)});J.promises.push(Q);let G=!1;return J.onFinished??=[],J.onFinished.push(()=>{if(!G){let U=Error(`Promise returned by \`${X}\` was not awaited. This assertion is asynchronous and must be awaited; otherwise, it is not guaranteed to complete before the test finishes: + +await ${X} +`);if(W.stack)U.stack=W.stack.replace(W.message,U.message);throw U}}),{then(U,z){return G=!0,Q.then(U,z)},catch(U){return G=!0,Q.catch(U)},finally(U){return G=!0,Q.finally(U)},[Symbol.toStringTag]:"Promise"}}return Q}function j6(Z,Q){Z.result||={state:"fail"},Z.result.state="fail",Z.result.errors||=[],Z.result.errors.push(R6(Q))}function k5(Z,Q,X){return function(...W){if(Q!=="withTest")Z.flag(this,"_name",Q);if(!Z.flag(this,"soft"))try{return X.apply(this,W)}finally{}let $=Z.flag(this,"vitest-test");if(!$)throw Error("expect.soft() can only be used inside a test");try{let J=X.apply(this,W);if(J&&typeof J==="object"&&typeof J.then==="function")return J.then(B6,(G)=>{j6($,G)});return J}catch(J){j6($,J)}}}var S5=(Z,Q)=>{let{AssertionError:X}=Z,W=x6();function $(Y,K){let B=(P)=>{let w=k5(Q,P,K);Q.addMethod(Z.Assertion.prototype,P,w),Q.addMethod(globalThis[l0].matchers,P,w)};if(Array.isArray(Y))Y.forEach((P)=>B(P));else B(Y)}["throw","throws","Throw"].forEach((Y)=>{Q.overwriteMethod(Z.Assertion.prototype,Y,(K)=>{return function(...B){let P=Q.flag(this,"promise"),w=Q.flag(this,"object"),N=Q.flag(this,"negate");if(P==="rejects")Q.flag(this,"object",()=>{throw w});else if(P==="resolves"&&typeof w!=="function")if(!N){let H=Q.flag(this,"message")||"expected promise to throw an error, but it didn't";throw new X(H,{showDiff:!1},Q.flag(this,"ssfi"))}else return;K.apply(this,B)}})}),$("withTest",function(Y){return Q.flag(this,"vitest-test",Y),this}),$("toEqual",function(Y){let K=Q.flag(this,"object"),B=E(K,Y,[...W,W9]);return this.assert(B,"expected #{this} to deeply equal #{exp}","expected #{this} to not deeply equal #{exp}",Y,K)}),$("toStrictEqual",function(Y){let K=Q.flag(this,"object"),B=E(K,Y,[...W,W9,q5,I6,H5],!0);return this.assert(B,"expected #{this} to strictly equal #{exp}","expected #{this} to not strictly equal #{exp}",Y,K)}),$("toBe",function(Y){let K=this._obj,B=Object.is(K,Y),P="";if(!B){if(E(K,Y,[...W,W9,q5,I6,H5],!0))P="toStrictEqual";else if(E(K,Y,[...W,W9]))P="toEqual"}return this.assert(B,aQ(P),"expected #{this} not to be #{exp} // Object.is equality",Y,K)}),$("toMatchObject",function(Y){let K=this._obj,B=E(K,Y,[...W,W9,$0]),P=Q.flag(this,"negate"),{subset:w,stripped:N}=tQ(K,Y,W);if(B&&P||!B&&!P){let H=Q.getMessage(this,[B,"expected #{this} to match object #{exp}","expected #{this} to not match object #{exp}",Y,w,!1]),F=N===0?H:`${H} +(${N} matching ${N===1?"property":"properties"} omitted from actual)`;throw new X(F,{showDiff:!0,expected:Y,actual:w})}}),$("toMatch",function(Y){let K=this._obj;if(typeof K!=="string")throw TypeError(`.toMatch() expects to receive a string, but got ${typeof K}`);return this.assert(typeof Y==="string"?K.includes(Y):K.match(Y),"expected #{this} to match #{exp}","expected #{this} not to match #{exp}",Y,K)}),$("toContain",function(Y){let K=this._obj;if(typeof Node<"u"&&K instanceof Node){if(!(Y instanceof Node))throw TypeError(`toContain() expected a DOM node as the argument, but got ${typeof Y}`);return this.assert(K.contains(Y),"expected #{this} to contain element #{exp}","expected #{this} not to contain element #{exp}",Y,K)}if(typeof DOMTokenList<"u"&&K instanceof DOMTokenList){H9(Y,"class name",["string"]);let P=Q.flag(this,"negate")?K.value.replace(Y,"").trim():`${K.value} ${Y}`;return this.assert(K.contains(Y),`expected "${K.value}" to contain "${Y}"`,`expected "${K.value}" not to contain "${Y}"`,P,K.value)}if(typeof K==="string"&&typeof Y==="string")return this.assert(K.includes(Y),"expected #{this} to contain #{exp}","expected #{this} not to contain #{exp}",Y,K);if(K!=null&&typeof K!=="string")Q.flag(this,"object",Array.from(K));return this.contain(Y)}),$("toContainEqual",function(Y){let K=Q.flag(this,"object"),B=Array.from(K).findIndex((P)=>{return E(P,Y,W)});this.assert(B!==-1,"expected #{this} to deep equally contain #{exp}","expected #{this} to not deep equally contain #{exp}",Y)}),$("toBeTruthy",function(){let Y=Q.flag(this,"object");this.assert(Boolean(Y),"expected #{this} to be truthy","expected #{this} to not be truthy",!0,Y)}),$("toBeFalsy",function(){let Y=Q.flag(this,"object");this.assert(!Y,"expected #{this} to be falsy","expected #{this} to not be falsy",!1,Y)}),$("toBeGreaterThan",function(Y){let K=this._obj;return H9(K,"actual",["number","bigint"]),H9(Y,"expected",["number","bigint"]),this.assert(K>Y,`expected ${K} to be greater than ${Y}`,`expected ${K} to be not greater than ${Y}`,Y,K,!1)}),$("toBeGreaterThanOrEqual",function(Y){let K=this._obj;return H9(K,"actual",["number","bigint"]),H9(Y,"expected",["number","bigint"]),this.assert(K>=Y,`expected ${K} to be greater than or equal to ${Y}`,`expected ${K} to be not greater than or equal to ${Y}`,Y,K,!1)}),$("toBeLessThan",function(Y){let K=this._obj;return H9(K,"actual",["number","bigint"]),H9(Y,"expected",["number","bigint"]),this.assert(KString(O).replace(/([.[\]])/g,"\\$1")).join(".");let K=this._obj;if(K==null)throw TypeError(`.toHaveProperty() expects to receive a valid object, but got ${K}`);let[B,P]=Y,w=()=>{if(Object.hasOwn(K,B))return{value:K[B],exists:!0};return Q.getPathInfo(K,B)},{value:N,exists:H}=w(),F=H&&(Y.length===1||E(P,N,W)),M=Y.length===1?"":` with value ${I8(P,{truncate:40})}`;return this.assert(F,`expected #{this} to have property "${B}"${M}`,`expected #{this} to not have property "${B}"${M}`,P,H?N:void 0)}),$("toBeCloseTo",function(Y,K=2){let B=this._obj,P=!1,w=0,N=0;if(Y===Number.POSITIVE_INFINITY&&B===Number.POSITIVE_INFINITY)P=!0;else if(Y===Number.NEGATIVE_INFINITY&&B===Number.NEGATIVE_INFINITY)P=!0;else w=10**-K/2,N=Math.abs(B-Y),P=N0,w=Q.flag(this,"negate"),N=Q.getMessage(this,[P,`expected "${K}" to be called at least once`,`expected "${K}" to not be called at all, but actually been called ${B} times`,!0,P]);if(P&&w)N=O6(Y,N);if(P&&w||!P&&!w)throw new X(N)});function U(Y,K){return Y.length===K.length&&Y.every((B,P)=>E(B,K[P],[...W,W9]))}$(["toHaveBeenCalledWith","toBeCalledWith"],function(...Y){let K=G(this),B=K.getMockName(),P=K.mock.calls.some((N)=>U(N,Y)),w=Q.flag(this,"negate");if(P&&w||!P&&!w){let N=Q.getMessage(this,[P,`expected "${B}" to be called with arguments: #{exp}`,`expected "${B}" to not be called with arguments: #{exp}`,Y]);throw new X(O6(K,N,Y))}}),$("toHaveBeenCalledExactlyOnceWith",function(...Y){let K=G(this),B=K.getMockName(),P=K.mock.calls.length,N=K.mock.calls.some((F)=>U(F,Y))&&P===1,H=Q.flag(this,"negate");if(N&&H||!N&&!H){let F=Q.getMessage(this,[N,`expected "${B}" to be called once with arguments: #{exp}`,`expected "${B}" to not be called once with arguments: #{exp}`,Y]);throw new X(O6(K,F,Y))}}),$("toHaveBeenNthCalledWith",function(Y,...K){let B=G(this),P=B.getMockName(),w=B.mock.calls[Y-1],N=B.mock.calls.length,H=Y<=N;this.assert(w&&U(w,K),`expected ${f9(Y)} "${P}" call to have been called with #{exp}${H?"":`, but called only ${N} times`}`,`expected ${f9(Y)} "${P}" call to not have been called with #{exp}`,K,w,H)}),$("toHaveBeenLastCalledWith",function(...Y){let K=G(this),B=K.getMockName(),P=K.mock.calls.at(-1);this.assert(P&&U(P,Y),`expected last "${B}" call to have been called with #{exp}`,`expected last "${B}" call to not have been called with #{exp}`,Y,P)});function z(Y,K,B){let P=Y.mock.invocationCallOrder,w=K.mock.invocationCallOrder;if(P.length===0)return!B;if(w.length===0)return!1;return P[0]"u"||Y instanceof RegExp)return this.throws(Y);let K=this._obj,B=Q.flag(this,"promise"),P=Q.flag(this,"negate"),w=null;if(B==="rejects")w=K;else if(B==="resolves"&&typeof K!=="function")if(!P){let H=Q.flag(this,"message")||"expected promise to throw an error, but it didn't";throw new X(H,{showDiff:!1},Q.flag(this,"ssfi"))}else return;else{let H=!1;try{K()}catch(F){H=!0,w=F}if(!H&&!P){let F=Q.flag(this,"message")||"expected function to throw an error, but it didn't";throw new X(F,{showDiff:!1},Q.flag(this,"ssfi"))}}if(typeof Y==="function"){let H=Y.name||Y.prototype.constructor.name;return this.assert(w&&w instanceof Y,`expected error to be instance of ${H}`,`expected error not to be instance of ${H}`,Y,w)}if(d9(Y)){let H=E(w,Y,[...W,W9]);return this.assert(H,"expected a thrown error to be #{exp}","expected a thrown error not to be #{exp}",Y,w)}if(typeof Y==="object"&&"asymmetricMatch"in Y&&typeof Y.asymmetricMatch==="function"){let H=Y;return this.assert(w&&H.asymmetricMatch(w),"expected error to match asymmetric matcher","expected error not to match asymmetric matcher",H,w)}let N=E(w,Y,[...W,W9]);return this.assert(N,"expected a thrown value to equal #{exp}","expected a thrown value not to equal #{exp}",Y,w)}),[{name:"toHaveResolved",condition:(Y)=>Y.mock.settledResults.length>0&&Y.mock.settledResults.some(({type:K})=>K==="fulfilled"),action:"resolved"},{name:["toHaveReturned","toReturn"],condition:(Y)=>Y.mock.calls.length>0&&Y.mock.results.some(({type:K})=>K!=="throw"),action:"called"}].forEach(({name:Y,condition:K,action:B})=>{$(Y,function(){let P=G(this),w=P.getMockName(),N=K(P);this.assert(N,`expected "${w}" to be successfully ${B} at least once`,`expected "${w}" to not be successfully ${B}`,N,!N,!1)})}),[{name:"toHaveResolvedTimes",condition:(Y,K)=>Y.mock.settledResults.reduce((B,{type:P})=>P==="fulfilled"?++B:B,0)===K,action:"resolved"},{name:["toHaveReturnedTimes","toReturnTimes"],condition:(Y,K)=>Y.mock.results.reduce((B,{type:P})=>P==="throw"?B:++B,0)===K,action:"called"}].forEach(({name:Y,condition:K,action:B})=>{$(Y,function(P){let w=G(this),N=w.getMockName(),H=K(w,P);this.assert(H,`expected "${N}" to be successfully ${B} ${P} times`,`expected "${N}" to not be successfully ${B} ${P} times`,`expected resolved times: ${P}`,`received resolved times: ${H}`,!1)})}),[{name:"toHaveResolvedWith",condition:(Y,K)=>Y.mock.settledResults.some(({type:B,value:P})=>B==="fulfilled"&&E(K,P)),action:"resolve"},{name:["toHaveReturnedWith","toReturnWith"],condition:(Y,K)=>Y.mock.results.some(({type:B,value:P})=>B==="return"&&E(K,P)),action:"return"}].forEach(({name:Y,condition:K,action:B})=>{$(Y,function(P){let w=G(this),N=K(w,P),H=Q.flag(this,"negate");if(N&&H||!N&&!H){let F=w.getMockName(),M=Q.getMessage(this,[N,`expected "${F}" to ${B} with: #{exp} at least once`,`expected "${F}" to not ${B} with: #{exp}`,P]),O=B==="return"?w.mock.results:w.mock.settledResults;throw new X(ZX(w,O,M,P))}})}),[{name:"toHaveLastResolvedWith",condition:(Y,K)=>{let B=Y.mock.settledResults.at(-1);return Boolean(B&&B.type==="fulfilled"&&E(B.value,K))},action:"resolve"},{name:"toHaveLastReturnedWith",condition:(Y,K)=>{let B=Y.mock.results.at(-1);return Boolean(B&&B.type==="return"&&E(B.value,K))},action:"return"}].forEach(({name:Y,condition:K,action:B})=>{$(Y,function(P){let w=G(this),H=(B==="return"?w.mock.results:w.mock.settledResults).at(-1),F=w.getMockName();this.assert(K(w,P),`expected last "${F}" call to ${B} #{exp}`,`expected last "${F}" call to not ${B} #{exp}`,P,H?.value)})}),[{name:"toHaveNthResolvedWith",condition:(Y,K,B)=>{let P=Y.mock.settledResults[K-1];return P&&P.type==="fulfilled"&&E(P.value,B)},action:"resolve"},{name:"toHaveNthReturnedWith",condition:(Y,K,B)=>{let P=Y.mock.results[K-1];return P&&P.type==="return"&&E(P.value,B)},action:"return"}].forEach(({name:Y,condition:K,action:B})=>{$(Y,function(P,w){let N=G(this),H=N.getMockName(),M=(B==="return"?N.mock.results:N.mock.settledResults)[P-1],O=`${f9(P)} call`;this.assert(K(N,P,w),`expected ${O} "${H}" call to ${B} #{exp}`,`expected ${O} "${H}" call to not ${B} #{exp}`,w,M?.value)})}),$("withContext",function(Y){for(let K in Y)Q.flag(this,K,Y[K]);return this}),Q.addProperty(Z.Assertion.prototype,"resolves",function(){let K=Error("resolves");Q.flag(this,"promise","resolves"),Q.flag(this,"error",K);let B=Q.flag(this,"vitest-test"),P=Q.flag(this,"object");if(Q.flag(this,"poll"))throw SyntaxError("expect.poll() is not supported in combination with .resolves");if(typeof P?.then!=="function")throw TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof P}'.`);let w=new Proxy(this,{get:(N,H,F)=>{let M=Reflect.get(N,H,F);if(typeof M!=="function")return M instanceof Z.Assertion?w:M;return(...O)=>{Q.flag(this,"_name",H);let A=Promise.resolve(P).then((I)=>{return Q.flag(this,"object",I),M.call(this,...O)},(I)=>{let L=new X(`promise rejected "${Q.inspect(I)}" instead of resolving`,{showDiff:!1});throw L.cause=I,L}).catch((I)=>{if(d9(I)&&K.stack)I.stack=K.stack.replace(K.message,I.message);throw I});return N5(B,A,P5(Q,this,!!O.length),K,Q.flag(this,"soft"))}}});return w}),Q.addProperty(Z.Assertion.prototype,"rejects",function(){let K=Error("rejects");Q.flag(this,"promise","rejects"),Q.flag(this,"error",K);let B=Q.flag(this,"vitest-test"),P=Q.flag(this,"object"),w=typeof P==="function"?P():P;if(Q.flag(this,"poll"))throw SyntaxError("expect.poll() is not supported in combination with .rejects");if(typeof w?.then!=="function")throw TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof w}'.`);let N=new Proxy(this,{get:(H,F,M)=>{let O=Reflect.get(H,F,M);if(typeof O!=="function")return O instanceof Z.Assertion?N:O;return(...A)=>{Q.flag(this,"_name",F);let I=Promise.resolve(w).then((L)=>{throw new X(`promise resolved "${Q.inspect(L)}" instead of rejecting`,{showDiff:!0,expected:Error("rejected promise"),actual:L})},(L)=>{return Q.flag(this,"object",L),O.call(this,...A)}).catch((L)=>{if(d9(L)&&K.stack)L.stack=K.stack.replace(K.message,L.message);throw L});return N5(B,I,P5(Q,this,!!A.length),K,Q.flag(this,"soft"))}}});return N})};function O6(Z,Q,X){if(Z.mock.calls.length)Q+=f.gray(` + +Received: + +${Z.mock.calls.map((W,$)=>{let J=f.bold(` ${f9($+1)} ${Z.getMockName()} call: + +`);if(X)J+=E9(X,W,{omitAnnotationLines:!0});else J+=o(W).split(` +`).map((G)=>` ${G}`).join(` +`);return J+=` +`,J}).join(` +`)}`);return Q+=f.gray(` + +Number of calls: ${f.bold(Z.mock.calls.length)} +`),Q}function ZX(Z,Q,X,W){if(Q.length)X+=f.gray(` + +Received: + +${Q.map(($,J)=>{let G=f.bold(` ${f9(J+1)} ${Z.getMockName()} call return: + +`);if(W)G+=E9(W,$.value,{omitAnnotationLines:!0});else G+=o($).split(` +`).map((U)=>` ${U}`).join(` +`);return G+=` +`,G}).join(` +`)}`);return X+=f.gray(` + +Number of calls: ${f.bold(Z.mock.calls.length)} +`),X}function QX(Z,Q){let X=Z._obj,W=z9.flag(Z,"negate"),$=z9.flag(Z,"promise")||"",J=z9.flag(Z,"message"),G={...R5(),diff:E9,stringify:o,iterableEquality:W9,subsetEquality:$0},U=z9.flag(Z,"vitest-test"),z=U?.fullTestName??"";if(U?.type!=="test")U=void 0;let Y={...J0(Q),currentTestName:z,customTesters:x6(),isNot:W,utils:G,promise:$,equals:E,suppressedErrors:[],soft:z9.flag(Z,"soft"),poll:z9.flag(Z,"poll"),assertion:Z};return Object.assign(Y,{task:U}),{state:Y,isNot:W,obj:X,customMessage:J}}class _6 extends Error{actual;expected;__vitest_error_context__;constructor(Z,Q,X,W){super(Z);this.actual=Q,this.expected=X,this.__vitest_error_context__=W}}function XX(Z,Q,X){return(W,$)=>{Object.entries(X).forEach(([J,G])=>{function U(...B){let{state:P,isNot:w,obj:N,customMessage:H}=QX(this,Q),F=G.call(P,N,...B);if(F&&typeof F==="object"&&typeof F.then==="function")return F.then(({pass:C,message:S,actual:b,expected:a,meta:u})=>{if(C&&w||!C&&!w){let v=(H?`${H}: `:"")+S();throw new _6(v,b,a,{assertionName:J,meta:u})}});let{pass:M,message:O,actual:A,expected:I,meta:L}=F;if(M&&w||!M&&!w){let T=(H?`${H}: `:"")+O();throw new _6(T,A,I,{assertionName:J,meta:L})}}let z=k5($,J,U);if($.addMethod(globalThis[l0].matchers,J,z),$.addMethod(Z.Assertion.prototype,J,z),G.__vitest_poll_takeover__){let B=Z.Assertion.prototype[J];Object.defineProperty(B,"__vitest_poll_takeover__",{value:!0})}class Y extends V9{constructor(B=!1,...P){super(P,B)}asymmetricMatch(B){let{pass:P}=G.call(this.getMatcherContext(Q),B,...this.sample);return this.inverse?!P:P}toString(){return`${this.inverse?"not.":""}${J}`}getExpectedType(){return"any"}toAsymmetricMatcher(){return`${this.toString()}<${this.sample.map((B)=>o(B)).join(", ")}>`}}let K=(...B)=>new Y(!1,...B);Object.defineProperty(Q,J,{configurable:!0,enumerable:!0,value:K,writable:!0}),Object.defineProperty(Q.not,J,{configurable:!0,enumerable:!0,value:(...B)=>new Y(!0,...B),writable:!0}),Object.defineProperty(globalThis[p0],J,{configurable:!0,enumerable:!0,value:K,writable:!0})})}}var j5=(Z,Q)=>{Q.addMethod(Z.expect,"extend",(X,W)=>{L9(XX(Z,X,W))})};u6();L9(j5);L9(S5);L9(A5);function WX(){let Z=(Q,X)=>{let{assertionCalls:W}=J0(Z);return o0({assertionCalls:W+1},Z),I9(Q,X)};return Object.assign(Z,I9),Object.assign(Z,globalThis[p0]),Z.getState=()=>J0(Z),Z.setState=(Q)=>o0(Q,Z),o0({assertionCalls:0,isExpectingAssertions:!1,isExpectingAssertionsError:null,expectedAssertionsNumber:null,expectedAssertionsNumberErrorGen:null},Z),Z.assert=V,Z.extend=(Q)=>I9.extend(Z,Q),Z.unreachable=(Q)=>V.fail("expected"+(Q?' "'+Q+'" ':" ")+"not to be reached"),Z}globalThis.__bundles=globalThis.__bundles||{};globalThis.__bundles["vitest-expect"]={createExpect:WX,fn:W5,spyOn:$5,isMockFunction:D9};})(); diff --git a/apps/sim/lib/execution/sandbox/run-task.ts b/apps/sim/lib/execution/sandbox/run-task.ts index 8e0e533c5d5..d28d5175d54 100644 --- a/apps/sim/lib/execution/sandbox/run-task.ts +++ b/apps/sim/lib/execution/sandbox/run-task.ts @@ -20,6 +20,8 @@ export interface RunSandboxTaskOptions { signal?: AbortSignal /** Records canonical workspace files whose bytes were successfully read by a task broker. */ onWorkspaceFileAccess?: SandboxBrokerContext['onWorkspaceFileAccess'] + /** Run id that brokers see as `ctx.requestId`, for callers that keep per-run broker state. */ + requestId?: string } /** @@ -51,7 +53,7 @@ export async function runSandboxTask( options: RunSandboxTaskOptions = {} ): Promise { const task = getSandboxTask(taskId) - const requestId = generateShortId(12) + const requestId = options.requestId ?? generateShortId(12) const brokerContext: SandboxBrokerContext = { workspaceId: input.workspaceId, @@ -78,6 +80,14 @@ export async function runSandboxTask( bootstrap: task.bootstrap, brokers: task.brokers.map((b) => b.name), finalize: task.finalize, + ...(task.userModules + ? { + userModule: { + filename: input.codeFilename ?? `${task.id}.js`, + modules: { ...task.userModules }, + }, + } + : {}), }, } diff --git a/apps/sim/lib/execution/sandbox/types.ts b/apps/sim/lib/execution/sandbox/types.ts index 08f7e1ff7fb..ee7082ac094 100644 --- a/apps/sim/lib/execution/sandbox/types.ts +++ b/apps/sim/lib/execution/sandbox/types.ts @@ -6,7 +6,7 @@ * which host-side brokers to expose, and how to serialize the final result. */ -export type SandboxBundleName = 'pptxgenjs' | 'docx' | 'pdf-lib' +export type SandboxBundleName = 'pptxgenjs' | 'docx' | 'pdf-lib' | 'vitest-expect' export interface SandboxBroker { /** @@ -35,6 +35,8 @@ export interface SandboxBrokerContext { export interface SandboxTaskInput { workspaceId: string code: string + /** Module filename for `code` when the task sets `userModules`; appears in stack traces. */ + codeFilename?: string } export interface SandboxTask { @@ -52,6 +54,11 @@ export interface SandboxTask * helper functions users expect (e.g. `getFileBase64`). */ bootstrap: string + /** + * When set, user code runs as an ES module that may import only these modules + * (specifier -> module source). Each source reads what `bootstrap` installed. + */ + userModules?: Readonly> /** * JS source that, when evaluated inside an async IIFE after user code, must * return a `Uint8Array`. The bytes are transferred out via `ExternalCopy`. diff --git a/apps/sim/lib/internal/file/operations.test.ts b/apps/sim/lib/internal/file/operations.test.ts index 8488db23333..db2d5519787 100644 --- a/apps/sim/lib/internal/file/operations.test.ts +++ b/apps/sim/lib/internal/file/operations.test.ts @@ -443,11 +443,9 @@ describe('file manage folder wiring', () => { ) expect(response.status).toBe(200) - expect(mockResolveWorkspaceFileReference).toHaveBeenCalledWith( - 'workspace-1', - 'a-self', - undefined - ) + expect(mockResolveWorkspaceFileReference).toHaveBeenCalledWith('workspace-1', 'a-self', { + includeTestFiles: true, + }) }) it('refuses an ambiguous name instead of editing an arbitrary file', async () => { @@ -484,11 +482,9 @@ describe('file manage folder wiring', () => { }) ) - expect(mockResolveWorkspaceFileReference).toHaveBeenCalledWith( - 'workspace-1', - 'a-self', - undefined - ) + expect(mockResolveWorkspaceFileReference).toHaveBeenCalledWith('workspace-1', 'a-self', { + includeTestFiles: true, + }) }) }) @@ -582,7 +578,7 @@ describe('file manage folder wiring', () => { expect(mockResolveWorkspaceFileReference).toHaveBeenCalledWith( 'workspace-1', 'notes-in-reports', - undefined + { includeTestFiles: true } ) }) diff --git a/apps/sim/lib/logs/get-trigger-options.ts b/apps/sim/lib/logs/get-trigger-options.ts index b5f88ced199..1d84adc6ae4 100644 --- a/apps/sim/lib/logs/get-trigger-options.ts +++ b/apps/sim/lib/logs/get-trigger-options.ts @@ -33,6 +33,7 @@ export function getTriggerOptions(): TriggerOption[] { { value: 'mothership', label: 'Sim agent', color: '#ec4899' }, { value: 'workflow', label: 'Workflow', color: '#0369a1' }, { value: 'custom_block', label: 'Custom block', color: '#0369a1' }, + { value: 'test', label: 'Test', color: '#4b5563' }, ] for (const trigger of triggers) { diff --git a/apps/sim/lib/mothership/agent-cli/resource-effects.ts b/apps/sim/lib/mothership/agent-cli/resource-effects.ts index de1ddb069a1..b397022a51c 100644 --- a/apps/sim/lib/mothership/agent-cli/resource-effects.ts +++ b/apps/sim/lib/mothership/agent-cli/resource-effects.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import type { z } from 'zod' import type { AnyApiRouteContract } from '@/lib/api/contracts/types' import * as credentials from '@/lib/api/contracts/v2/credentials' @@ -15,8 +16,11 @@ import * as tables from '@/lib/api/contracts/v2/tables' import * as workflows from '@/lib/api/contracts/v2/workflows' import { parseFolderPath } from '@/lib/folders/paths' import type { ResourceAddress, ResourceChange } from '@/lib/mothership/generated/resources' +import { findNonTabFileIds } from '@/lib/mothership/resources/file-tabs' import { encodeVfsPathSegments } from '@/lib/vfs/path' +const logger = createLogger('ResourceEffects') + interface EffectContext { params: Record body?: unknown @@ -339,6 +343,27 @@ function matchParams(pattern: string, pathname: string): Record return params } +/** + * Only workspace files and chat uploads open as file tabs; other rows open as their owner. The + * request already committed, so a failed lookup opens no file tab rather than failing it. + */ +async function onlyFileTabs(changes: ResourceChange[]): Promise { + const fileUpsertId = (change: ResourceChange) => + change.op === 'upsert' && change.resource.type === 'file' ? change.resource.id : undefined + const ids = changes.flatMap((change) => fileUpsertId(change) ?? []) + let nonTabIds: Set + try { + nonTabIds = await findNonTabFileIds(ids) + } catch (error) { + logger.error('Could not check which files open as tabs; opening none', { error }) + nonTabIds = new Set(ids) + } + return changes.filter((change) => { + const id = fileUpsertId(change) + return id === undefined || !nonTabIds.has(id) + }) +} + /** The observer adds no request and never reads provider responses or download bodies. */ export function createResourceEffectTransport( endpoint: string, @@ -366,7 +391,8 @@ export function createResourceEffectTransport( const response = await transport(input, init) if (route && response.ok && (!route.readOnly || observeReads)) { const params = matchParams(route.path, url.pathname) - if (params) effects.push(...(await route.project(response, { params, body }))) + if (params) + effects.push(...(await onlyFileTabs(await route.project(response, { params, body })))) } return response } diff --git a/apps/sim/lib/mothership/agent-cli/services.ts b/apps/sim/lib/mothership/agent-cli/services.ts index b783f5a01f1..1b5f856f293 100644 --- a/apps/sim/lib/mothership/agent-cli/services.ts +++ b/apps/sim/lib/mothership/agent-cli/services.ts @@ -39,7 +39,8 @@ export async function executeAgentCliService( const scope = invocation.kind === 'service' && invocation.name === 'settings' ? invocation.input.scope - : invocation.kind === 'service' && invocation.name === 'dashboards' + : invocation.kind === 'service' && + (invocation.name === 'dashboards' || invocation.name === 'tests') ? 'workspace' : 'organization' if ( diff --git a/apps/sim/lib/mothership/application/execute-workflow-test-use-case.ts b/apps/sim/lib/mothership/application/execute-workflow-test-use-case.ts new file mode 100644 index 00000000000..b01ac10e03b --- /dev/null +++ b/apps/sim/lib/mothership/application/execute-workflow-test-use-case.ts @@ -0,0 +1,13 @@ +import { createCopilotApplicationAdapter } from '@/lib/mothership/application/application-adapter' +import { COPILOT_APPLICATION_DELEGATION_TTL_MS } from '@/lib/mothership/auth/application-delegation' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' + +export const executeWorkflowTestUseCase = createCopilotApplicationAdapter({ + domain: 'workflow_tests', + operations: workflowTestOperations, + delegation: { + audience: 'sim:workspaces', + ttlMs: COPILOT_APPLICATION_DELEGATION_TTL_MS, + createDelegationId: (context) => `copilot-tool:${context.toolCallId}`, + }, +}) diff --git a/apps/sim/lib/mothership/chat/payload.test.ts b/apps/sim/lib/mothership/chat/payload.test.ts index 509f5cde55e..905cf076884 100644 --- a/apps/sim/lib/mothership/chat/payload.test.ts +++ b/apps/sim/lib/mothership/chat/payload.test.ts @@ -54,6 +54,9 @@ vi.mock('@/lib/mothership/chat/workspace-inventory', () => ({ vi.mock('@/lib/dashboards/application/availability', () => ({ readDashboardAvailability: { execute: mockDashboardAvailability }, })) +vi.mock('@/lib/workflow-tests/application/availability', () => ({ + readWorkflowTestAvailability: { execute: vi.fn(async () => false) }, +})) vi.mock('@/lib/billing/core/subscription', () => billingSubscriptionMock) const mockGetHighestPrioritySubscription = billingSubscriptionMockFns.mockGetHighestPrioritySubscription diff --git a/apps/sim/lib/mothership/chat/post.ts b/apps/sim/lib/mothership/chat/post.ts index d7bb135fc03..55a1617594d 100644 --- a/apps/sim/lib/mothership/chat/post.ts +++ b/apps/sim/lib/mothership/chat/post.ts @@ -135,6 +135,7 @@ const GENERIC_RESOURCE_TITLE: Record['t integration: 'Integration', file: 'File', dashboard: 'Dashboard', + test: 'Test', knowledgebase: 'Knowledge Base', folder: 'Folder', filefolder: 'File Folder', diff --git a/apps/sim/lib/mothership/entitlements.ts b/apps/sim/lib/mothership/entitlements.ts index e97f98941e4..48cdf2aaaaa 100644 --- a/apps/sim/lib/mothership/entitlements.ts +++ b/apps/sim/lib/mothership/entitlements.ts @@ -1,6 +1,7 @@ import type { Principal } from '@sim/auth/principal' import { readDashboardAvailability } from '@/lib/dashboards/application/availability' import { ENTITLEMENTS, type Entitlement } from '@/lib/mothership/generated/protocol' +import { readWorkflowTestAvailability } from '@/lib/workflow-tests/application/availability' /** The owner of one chat turn: exactly one of a workspace or an organization. */ export interface EntitlementOwner { @@ -48,6 +49,10 @@ const EVALUATORS: Record = { workspace: ({ principal, workspaceId }) => readDashboardAvailability.execute({ principal, input: { workspaceId } }), }, + [ENTITLEMENTS.tests]: { + workspace: ({ principal, workspaceId }) => + readWorkflowTestAvailability.execute({ principal, input: { workspaceId } }), + }, } function evaluate(evaluator: EntitlementEvaluator, owner: EntitlementOwner): Promise { diff --git a/apps/sim/lib/mothership/generated/agent-cli.ts b/apps/sim/lib/mothership/generated/agent-cli.ts index 988ea2fb4ff..27744c0de02 100644 --- a/apps/sim/lib/mothership/generated/agent-cli.ts +++ b/apps/sim/lib/mothership/generated/agent-cli.ts @@ -53,6 +53,7 @@ export const AgentCliServiceInvocation = z.object({ "settings", "search_sources", "dashboards", + "tests", "workspaces", ]), input: z.record(z.string(), z.json()), diff --git a/apps/sim/lib/mothership/generated/protocol.ts b/apps/sim/lib/mothership/generated/protocol.ts index 5b8a7c07b59..ea7d7b563a5 100644 --- a/apps/sim/lib/mothership/generated/protocol.ts +++ b/apps/sim/lib/mothership/generated/protocol.ts @@ -137,7 +137,7 @@ export type DesktopContext = z.infer; * the list travels in a forgeable payload. The wire accepts any name, so Sim can add one * before the worker learns it. */ -export const ENTITLEMENTS = { dashboards: "dashboards" } as const; +export const ENTITLEMENTS = { dashboards: "dashboards", tests: "tests" } as const; export type Entitlement = (typeof ENTITLEMENTS)[keyof typeof ENTITLEMENTS]; export const Entitlements = z.array(z.string().min(1).max(64)).max(32); diff --git a/apps/sim/lib/mothership/generated/resources.ts b/apps/sim/lib/mothership/generated/resources.ts index 33505e8ad5d..06090025bee 100644 --- a/apps/sim/lib/mothership/generated/resources.ts +++ b/apps/sim/lib/mothership/generated/resources.ts @@ -16,6 +16,7 @@ export const ResourceType = z.enum([ "knowledgebase", "file", "dashboard", + "test", "folder", "filefolder", "log", diff --git a/apps/sim/lib/mothership/request/go/file-preview-adapter.ts b/apps/sim/lib/mothership/request/go/file-preview-adapter.ts index 0dedab3b707..3f2984f66e4 100644 --- a/apps/sim/lib/mothership/request/go/file-preview-adapter.ts +++ b/apps/sim/lib/mothership/request/go/file-preview-adapter.ts @@ -22,13 +22,17 @@ import type { StreamEvent, StreamingContext, } from '@/lib/mothership/request/types' +import { findNonTabFileIds } from '@/lib/mothership/resources/file-tabs' import { peekFileIntent } from '@/lib/mothership/tools/server/files/file-intent-store' import { buildFilePreviewText, loadWorkspaceFileTextForPreview, type WorkspaceFilePreviewBase, } from '@/lib/mothership/tools/server/files/file-preview' -import { findWorkspaceFileRecord } from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { + findWorkspaceFileRecord, + resolveWorkspaceFileReference, +} from '@/lib/uploads/contexts/workspace/workspace-file-manager' import { listAllWorkspaceFiles } from '@/lib/workspace-files/application/list-workspace-files' const logger = createLogger('CopilotFilePreviewAdapter') @@ -135,6 +139,23 @@ async function resolvePreviewTarget(args: { } } +/** + * Whether the preview target may open as a file tab. A file another resource owns streams no + * preview, since the preview would open it as a file tab. + */ +async function isFileTabTarget( + workspaceId: string | undefined, + target: FileIntent['target'] +): Promise { + let fileId = target.fileId + if (!fileId && target.kind === 'path' && target.path && workspaceId) { + fileId = ( + await resolveWorkspaceFileReference(workspaceId, target.path, { includeTestFiles: true }) + )?.id + } + return fileId === undefined || !(await findNonTabFileIds([fileId])).has(fileId) +} + function parseWorkspaceFileArgs(value: unknown): ParsedWorkspaceFileArgs | undefined { const args = asJsonRecord(value) if (!args) { @@ -446,6 +467,10 @@ export async function processFilePreviewStreamEvent(input: { }) const previewTargetKind = toPreviewTargetKind(target.kind) const { fileId, fileName } = target + if (!(await isFileTabTarget(execContext.workspaceId, target))) { + clearIntent() + return + } const isContentOp = isContentOperation(operation) // Per-channel: a re-declared prepare_file_edit just overwrites THIS channel's @@ -526,6 +551,10 @@ export async function processFilePreviewStreamEvent(input: { isContentOperation(workspaceResultIntent.operation) ) { const result = extractWorkspaceFileResult(streamEvent.payload.output) + if (result.fileId && (await findNonTabFileIds([result.fileId])).has(result.fileId)) { + clearIntent() + return + } if (result.fileId && workspaceResultIntent.target.kind === 'path') { const intent: FileIntent = { ...workspaceResultIntent, diff --git a/apps/sim/lib/mothership/request/tools/resources.ts b/apps/sim/lib/mothership/request/tools/resources.ts index c74853cfda4..2a3ded3cf7b 100644 --- a/apps/sim/lib/mothership/request/tools/resources.ts +++ b/apps/sim/lib/mothership/request/tools/resources.ts @@ -9,6 +9,7 @@ import { TraceAttr } from '@/lib/mothership/generated/trace-attributes-v1' import { TraceSpan } from '@/lib/mothership/generated/trace-spans-v1' import { withCopilotSpan } from '@/lib/mothership/request/otel' import type { StreamEvent, ToolCallResult } from '@/lib/mothership/request/types' +import { findNonTabFileIds } from '@/lib/mothership/resources/file-tabs' import { extractDeletedResourcesFromToolResult, extractResourcesFromToolResult, @@ -119,7 +120,7 @@ export async function handleResourceSideEffects( : isResourceToolName(toolName) ? extractResourcesFromToolResult(toolName, params, projectedResult.output) : [] - const resources = + const paired = projectedResources.length === rawResources.length ? rawResources.map((resource, index) => ({ ...projectedResources[index], @@ -130,6 +131,12 @@ export async function handleResourceSideEffects( : {}), })) : [] + const nonTabFileIds = await findNonTabFileIds( + paired.flatMap((resource) => (resource.type === 'file' ? [resource.id] : [])) + ) + const resources = paired.filter( + (resource) => resource.type !== 'file' || !nonTabFileIds.has(resource.id) + ) if (resources.length > 0) { upsertedCount = resources.length diff --git a/apps/sim/lib/mothership/resources/file-tabs.ts b/apps/sim/lib/mothership/resources/file-tabs.ts new file mode 100644 index 00000000000..73cbff282c0 --- /dev/null +++ b/apps/sim/lib/mothership/resources/file-tabs.ts @@ -0,0 +1,56 @@ +import { db } from '@sim/db' +import { workspaceFiles } from '@sim/db/schema' +import { createLogger } from '@sim/logger' +import { toStringOrNull } from '@sim/utils/coerce' +import { and, inArray, notInArray } from 'drizzle-orm' +import type { BaseServerTool } from '@/lib/mothership/tools/server/base-tool' + +const logger = createLogger('FileTabs') + +/** Row contexts that open as a file tab: workspace files and chat uploads. */ +const FILE_TAB_CONTEXTS = ['workspace', 'mothership'] + +/** + * The ids among `fileIds` whose rows must not open as a file tab: any context other than a + * workspace file or chat upload belongs to another resource (a test's source, for one), which + * opens as that resource instead. + */ +export async function findNonTabFileIds(fileIds: string[]): Promise> { + if (fileIds.length === 0) return new Set() + const rows = await db + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where( + and( + inArray(workspaceFiles.id, fileIds), + notInArray(workspaceFiles.context, FILE_TAB_CONTEXTS) + ) + ) + return new Set(rows.map((row) => row.id)) +} + +/** + * Marks a file-editing tool's result `fileTab: false` when the file it touched is not a file tab, + * so the browser, which promotes edited files to tabs from the result, leaves it alone. + */ +export function withFileTabFlag }>( + tool: BaseServerTool +): BaseServerTool { + return { + ...tool, + async execute(args, context) { + const result = await tool.execute(args, context) + const id = toStringOrNull(result.data?.id) + if (id === null) return result + let isTab: boolean + try { + isTab = !(await findNonTabFileIds([id])).has(id) + } catch (error) { + // The edit already saved, so a failed lookup opens no tab rather than failing it. + logger.error('Could not check whether an edited file is a file tab', { id, error }) + isTab = false + } + return isTab ? result : { ...result, data: { ...result.data, fileTab: false } } + }, + } +} diff --git a/apps/sim/lib/mothership/resources/types.ts b/apps/sim/lib/mothership/resources/types.ts index 6e4990a6d31..4cc79d7d243 100644 --- a/apps/sim/lib/mothership/resources/types.ts +++ b/apps/sim/lib/mothership/resources/types.ts @@ -6,6 +6,7 @@ export const MothershipResourceType = { table: 'table', file: 'file', dashboard: 'dashboard', + test: 'test', workflow: 'workflow', knowledgebase: 'knowledgebase', folder: 'folder', @@ -112,6 +113,7 @@ const RESOURCE_POLICY: Record = { table: { persisted: true }, file: { persisted: true }, dashboard: { persisted: true }, + test: { persisted: true }, workflow: { persisted: true }, knowledgebase: { persisted: true }, folder: { persisted: true }, diff --git a/apps/sim/lib/mothership/tools/server/files/workspace-file.ts b/apps/sim/lib/mothership/tools/server/files/workspace-file.ts index 6e1bb97588e..4dc211befd2 100644 --- a/apps/sim/lib/mothership/tools/server/files/workspace-file.ts +++ b/apps/sim/lib/mothership/tools/server/files/workspace-file.ts @@ -21,6 +21,7 @@ import { } from '@/lib/mothership/tools/server/base-tool' import { DocCompileUserError } from '@/lib/mothership/tools/server/files/doc-compile-error' import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { parseTestFileReference } from '@/lib/workflow-tests/paths' import { admitCreateWorkspaceFile, createWorkspaceFile, @@ -350,6 +351,12 @@ export const workspaceFileServerTool: BaseServerTool = { [dashboardsServerTool.name]: dashboardsServerTool, + [testsServerTool.name]: testsServerTool, [searchDocsServerTool.name]: searchDocsServerTool, [searchWorkspaceServerTool.name]: searchWorkspaceServerTool, [listWorkspacesServerTool.name]: listWorkspacesServerTool, @@ -72,8 +75,8 @@ const baseServerToolRegistry: Record = { [readDocumentServerTool.name]: readDocumentServerTool, // The streamed file-writing pair: prepare opens the write (live preview), // apply continues it. The preview machinery keys off these exact names. - [workspaceFileServerTool.name]: workspaceFileServerTool, - [editContentServerTool.name]: editContentServerTool, + [workspaceFileServerTool.name]: withFileTabFlag(workspaceFileServerTool), + [editContentServerTool.name]: withFileTabFlag(editContentServerTool), [generateImageServerTool.name]: generateImageServerTool, [generateVideoServerTool.name]: generateVideoServerTool, [generateAudioServerTool.name]: generateAudioServerTool, diff --git a/apps/sim/lib/mothership/tools/server/tests.ts b/apps/sim/lib/mothership/tools/server/tests.ts new file mode 100644 index 00000000000..531836f9100 --- /dev/null +++ b/apps/sim/lib/mothership/tools/server/tests.ts @@ -0,0 +1,179 @@ +import { sleep } from '@sim/utils/helpers' +import { mothershipTestsInputSchema } from '@/lib/api/contracts/mothership-tests' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { executeWorkflowTestUseCase } from '@/lib/mothership/application/execute-workflow-test-use-case' +import { requireTrustedCopilotExecutionContext } from '@/lib/mothership/auth/application-delegation' +import type { ResourceChange } from '@/lib/mothership/generated/resources' +import { + assertServerToolNotAborted, + type BaseServerTool, + type ServerToolContext, +} from '@/lib/mothership/tools/server/base-tool' +import { startWorkflowTestRuns } from '@/lib/workflow-tests/application/run-tests' +import { + createWorkflowTest, + deleteWorkflowTest, + getWorkflowTestDetail, + listWorkflowTests, + readWorkflowTestRuns, + updateWorkflowTest, +} from '@/lib/workflow-tests/application/tests' +import type { TestReport } from '@/lib/workflow-tests/protocol' + +const RUN_POLL_MS = 2_000 + +function workspaceFor(input: { workspaceId?: string }, context?: ServerToolContext): string { + const trusted = requireTrustedCopilotExecutionContext(context) + if (input.workspaceId && input.workspaceId !== trusted.workspaceId) + throw new OrchestrationError('not_found', 'Workspace not found in this invocation') + assertServerToolNotAborted(context) + return trusted.workspaceId +} + +/** Each failing case once: where it failed and why, in vitest's own words or the judge's. */ +function failures(report: TestReport | null) { + if (!report) return [] + return report.tests + .filter((test) => test.status === 'fail') + .map((test) => { + const check = test.checks.find((candidate) => candidate.status === 'fail') + return { + test: test.path.join(' > '), + line: check?.line ?? test.error?.line, + message: check?.judge ?? check?.message ?? test.error?.message ?? 'Failed', + executions: test.executions.map((execution) => execution.executionId), + } + }) +} + +export const testsServerTool: BaseServerTool = { + name: 'tests', + inputSchema: mothershipTestsInputSchema, + async execute(raw, context) { + const input = mothershipTestsInputSchema.parse(raw) + const workspaceId = workspaceFor(input, context) + switch (input.action) { + case 'create': { + const { test } = await executeWorkflowTestUseCase(context, createWorkflowTest, { + workspaceId, + name: input.name, + title: input.title, + description: input.description, + }) + const resources: ResourceChange[] = [ + { + op: 'upsert', + resource: { type: 'test', workspaceId, id: test.name, title: test.title }, + }, + ] + return { test: { name: test.name, title: test.title }, document: test.path, resources } + } + case 'update': { + const { test } = await executeWorkflowTestUseCase(context, updateWorkflowTest, { + workspaceId, + name: input.name, + title: input.title, + description: input.description, + }) + return { test: { name: test.name, title: test.title, description: test.description } } + } + case 'list': { + const { tests } = await executeWorkflowTestUseCase(context, listWorkflowTests, { + workspaceId, + version: input.version, + }) + return { + tests: tests.map((test) => ({ + name: test.name, + title: test.title, + document: test.path, + cases: test.caseCount, + status: test.status, + })), + } + } + case 'get': { + const detail = await executeWorkflowTestUseCase(context, getWorkflowTestDetail, { + workspaceId, + name: input.name, + version: input.version, + }) + return { + test: { + name: detail.test.name, + title: detail.test.title, + description: detail.test.description, + document: detail.test.path, + status: detail.test.status, + cases: detail.test.cases.map((testCase) => testCase.path.join(' > ')), + }, + latestRun: detail.latestRun + ? { + status: detail.latestRun.status, + upToDate: detail.latestRun.current, + ranAgainst: detail.latestRun.ranAgainst.map((workflow) => ({ + workflow: workflow.name, + version: workflow.draft ? 'draft' : workflow.version, + redeployedSince: workflow.stale, + })), + passed: detail.latestRun.passed, + failed: detail.latestRun.failed, + error: detail.latestRun.error, + failures: failures(detail.latestRun.report), + } + : null, + } + } + case 'run': { + const { runs } = await executeWorkflowTestUseCase(context, startWorkflowTestRuns, { + workspaceId, + version: input.version, + names: input.names, + only: input.only, + }) + const runIds = runs.map((run) => run.runId) + for (;;) { + assertServerToolNotAborted(context) + const { runs: current } = await executeWorkflowTestUseCase( + context, + readWorkflowTestRuns, + { + workspaceId, + runIds, + } + ) + if (current.every((run) => run.status !== 'running')) { + const resources: ResourceChange[] = current.map((run) => ({ + op: 'refresh', + resource: { type: 'test', id: run.name }, + })) + return { + resources, + version: input.version, + results: current.map((run) => ({ + name: run.name, + status: run.status, + passed: run.passed, + failed: run.failed, + skipped: run.skipped, + ...(run.error ? { error: run.error } : {}), + failures: failures(run.report), + })), + } + } + await sleep(RUN_POLL_MS) + } + } + case 'delete': { + await executeWorkflowTestUseCase(context, deleteWorkflowTest, { + workspaceId, + name: input.name, + }) + const resources: ResourceChange[] = [ + { op: 'remove', resource: { type: 'test', workspaceId, id: input.name } }, + ] + return { deleted: input.name, resources } + } + } + }, +} diff --git a/apps/sim/lib/mothership/vfs/path-utils.ts b/apps/sim/lib/mothership/vfs/path-utils.ts index 730c4a41fa9..f4ae4a0b4ca 100644 --- a/apps/sim/lib/mothership/vfs/path-utils.ts +++ b/apps/sim/lib/mothership/vfs/path-utils.ts @@ -37,13 +37,13 @@ export function canonicalizeVfsPath(path: string): string { } /** - * Canonical, per-segment-encoded VFS path of a workspace file. `uploads` is the - * chat-upload namespace, which has no folders. + * Canonical, per-segment-encoded VFS path of a workspace file. `uploads` (chat uploads) + * and `tests` (test files) are namespaces without folders. */ export function canonicalWorkspaceFilePath(parts: { folderPath?: string | null name: string - prefix?: 'files' | 'recently-deleted/files' | 'uploads' + prefix?: 'files' | 'recently-deleted/files' | 'uploads' | 'tests' }): string { const prefix = parts.prefix ?? 'files' const folderSegments = parts.folderPath diff --git a/apps/sim/lib/uploads/config.ts b/apps/sim/lib/uploads/config.ts index ab49401e8c3..0d456fb23a3 100644 --- a/apps/sim/lib/uploads/config.ts +++ b/apps/sim/lib/uploads/config.ts @@ -220,6 +220,7 @@ function getS3Config(context: StorageContext): StorageConfig { } case 'mothership': case 'workspace': + case 'test': case 'table-import': case 'organization-logos': return { @@ -284,6 +285,7 @@ function getBlobConfig(context: StorageContext): StorageConfig { } case 'mothership': case 'workspace': + case 'test': case 'table-import': case 'organization-logos': return { @@ -345,6 +347,7 @@ function getGcsConfig(context: StorageContext): StorageConfig { return { bucket: GCS_EXECUTION_FILES_CONFIG.bucket || GCS_CONFIG.bucket } case 'mothership': case 'workspace': + case 'test': case 'table-import': case 'organization-logos': return { bucket: GCS_CONFIG.bucket } diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts index 57161c2058c..b25f2f445b0 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -48,7 +48,7 @@ import { asOrchestrationError, OrchestrationError } from '@/lib/core/orchestrati import { generateRequestId } from '@/lib/core/utils/request' import { generateRestoreName } from '@/lib/core/utils/restore-name' import { isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' -import type { DbOrTx } from '@/lib/db/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { acquireFolderMutationLock } from '@/lib/folders/locks' import { parseFolderPath } from '@/lib/folders/paths' import { @@ -101,11 +101,17 @@ import { getWorkspaceFileSize, MAX_WORKSPACE_FILE_SIZE } from '@/lib/uploads/sha import { isMarkdownFile } from '@/lib/uploads/utils/file-utils' import type { ServableFile } from '@/lib/uploads/utils/file-utils.server' import { displaySegmentPattern } from '@/lib/vfs/path' +import { parseTestFileReference, TEST_FILE_SUFFIX } from '@/lib/workflow-tests/paths' +import { + getLiveWorkflowTestByBodyFileId, + getLiveWorkflowTestByName, +} from '@/lib/workflow-tests/repository' import { SIM_PAGE_CONTENT_TYPE } from '@/lib/workspace-files/page-compile' import { MAX_SIM_PAGE_UPLOAD_SNIFF_BYTES, restoreSimPageSourceBuffer, } from '@/lib/workspace-files/page-source-embed' +import { contentWritableWorkspaceFileContextCondition } from '@/lib/workspace-files/query-scope' import { getWorkspaceWithOwner } from '@/lib/workspaces/permissions/utils' import { isUuid } from '@/executor/constants' import type { UserFile } from '@/executor/types' @@ -168,7 +174,7 @@ export interface WorkspaceFileRecord { * Set on chat uploads (`context = 'mothership'`), which the VFS addresses as * `uploads/` rather than `files/…`; `name` then carries the upload's display name. */ - vfsNamespace?: 'uploads' + vfsNamespace?: 'uploads' | 'tests' /** Public share state, attached at the API boundary. `null` when never shared. */ share?: ShareRecord | null } @@ -188,6 +194,8 @@ export interface UploadedWorkspaceFileRecord extends WorkspaceFileRecord { export interface ActiveWorkspaceFileContext { fileId: string + /** `workspace_files.context`: `'test'` for a test file, which follows its test's policy. */ + fileContext: string workspaceId: string workspaceOrganizationId: string | null allowPersonalApiKeys: boolean @@ -281,6 +289,7 @@ interface WorkspaceFileMetadataInsert { originalName: string contentType: string size: number + context?: 'workspace' | 'test' } /** @@ -295,9 +304,9 @@ async function insertWorkspaceFileMetadataInTx( const [inserted] = await tx .insert(workspaceFiles) .values({ - ...omit(metadata, ['size']), + ...omit(metadata, ['size', 'context']), sizeBytes: metadata.size, - context: 'workspace', + context: metadata.context ?? 'workspace', displayName: metadata.originalName, deletedAt: null, uploadedAt: new Date(), @@ -612,6 +621,72 @@ export async function uploadWorkspaceFile( throw new FileConflictError(fileName) } +/** + * Creates a file another resource owns: a test file's source (`context = 'test'`). + * `insertOwner` runs in the file's insert transaction, so the owner row and its file commit together or not at all. + */ +export async function createOwnedWorkspaceFile(params: { + context: 'test' + workspaceId: string + userId: string + content: string + contentType: string + fileName: (fileId: string) => string + insertOwner: (tx: DbTransaction, fileId: string) => Promise +}): Promise<{ fileId: string; owner: T }> { + const fileId = `wf_${generateShortId()}` + const name = params.fileName(fileId) + const buffer = Buffer.from(params.content, 'utf-8') + const storageKey = generateWorkspaceFileKey(params.workspaceId, name) + const storageBillingContext = await resolveStorageBillingContext(params.workspaceId) + const uploadResult = await uploadFile({ + file: buffer, + fileName: storageKey, + contentType: params.contentType, + context: 'workspace', + preserveKey: true, + customKey: storageKey, + metadata: { + originalName: name, + uploadedAt: new Date().toISOString(), + purpose: 'workspace', + userId: params.userId, + workspaceId: params.workspaceId, + }, + persistMetadata: false, + }) + try { + const { owner, updatedUsage } = await db.transaction(async (tx) => { + const inserted = await insertWorkspaceFileMetadataInTx(tx, { + id: fileId, + key: uploadResult.key, + userId: params.userId, + workspaceId: params.workspaceId, + folderId: null, + originalName: name, + contentType: params.contentType, + size: buffer.length, + context: params.context, + }) + if (!inserted) throw new Error(`Owned ${params.context} file ${fileId} was not inserted`) + const updatedUsage = await incrementStorageUsageForBillingContextInTx( + tx, + storageBillingContext, + buffer.length + ) + return { owner: await params.insertOwner(tx, fileId), updatedUsage } + }) + void maybeNotifyStorageLimitForBillingContext(storageBillingContext, updatedUsage) + return { fileId, owner } + } catch (error) { + await cleanupWorkspaceStorageObject( + uploadResult.key, + `${params.context} file owner insert failure` + ) + throw error + } +} + /** * Finalize a workspace file that was uploaded through a transfer session * (signed PUT or completed multipart). Verifies the object exists, @@ -1217,6 +1292,7 @@ async function mapSingleWorkspaceFileRecord( if (file.context === 'mothership') { return mapChatUploadRecord(file, workspaceId) } + if (file.context === 'test') return mapTestFileRecord(file, workspaceId) if (!file.folderId) { return mapWorkspaceFileRecord(file, workspaceId, new Map()) } @@ -1231,6 +1307,17 @@ async function mapSingleWorkspaceFileRecord( ) } +/** A test file reads as `tests/.test.js`, the path Sim addresses it by. */ +async function mapTestFileRecord( + file: WorkspaceFileRow, + workspaceId: string +): Promise { + const record = mapWorkspaceFileRecord(file, workspaceId, new Map()) + const owner = await getLiveWorkflowTestByBodyFileId(file.id) + if (!owner) return record + return { ...record, name: `${owner.name}${TEST_FILE_SUFFIX}`, vfsNamespace: 'tests' } +} + /** * Store an image file's intrinsic pixel dimensions (a pure rendering hint used to reserve layout space * before the image loads). The client reports the browser's own EXIF-corrected `naturalWidth/Height`, and @@ -1299,27 +1386,34 @@ export async function getWorkspaceFileByName( */ export interface WorkspaceFileLookupOptions { includeChatUploads?: boolean + /** + * Admit test files (`context = 'test'`), by id or as `tests/.test.js`. Only content + * reads and writes set this, and their use cases apply the test policy to whatever they admit. + */ + includeTestFiles?: boolean /** Internal Mothership scope for uploads/; ordinary API lookup remains workspace-wide. */ chatId?: string } -/** Row context a single-file lookup admits: workspace files, plus chat uploads on opt-in. */ -function workspaceFileContextCondition(includeChatUploads?: boolean) { - return includeChatUploads - ? inArray(workspaceFiles.context, ['workspace', 'mothership']) - : eq(workspaceFiles.context, 'workspace') +/** Row context a single-file lookup admits: workspace files, plus chat uploads and test files on opt-in. */ +function workspaceFileContextCondition(options?: WorkspaceFileLookupOptions) { + const contexts = [ + 'workspace', + ...(options?.includeChatUploads ? ['mothership'] : []), + ...(options?.includeTestFiles ? ['test'] : []), + ] + return contexts.length === 1 + ? eq(workspaceFiles.context, 'workspace') + : inArray(workspaceFiles.context, contexts) } /** Workspace-file rows for one scope: live, Recently Deleted, or both. */ function workspaceFileScopeCondition( workspaceId: string, scope: WorkspaceFileScope, - includeChatUploads?: boolean + lookup?: WorkspaceFileLookupOptions ) { - const base = [ - eq(workspaceFiles.workspaceId, workspaceId), - workspaceFileContextCondition(includeChatUploads), - ] + const base = [eq(workspaceFiles.workspaceId, workspaceId), workspaceFileContextCondition(lookup)] if (scope === 'all') return and(...base) return scope === 'archived' ? and(...base, isNotNull(workspaceFiles.deletedAt)) @@ -1662,12 +1756,24 @@ async function getWorkspaceFileByExactReference( * records without one rather than pairing a row with a version a second query read later. * With `includeChatUploads`, an `uploads/` path (or a chat upload's own id) reaches * the chat upload it names; chat uploads are never found through the listing fallback. + * With `includeTestFiles`, `tests/.test.js` (or the file's own id) reaches that test file. */ export async function resolveWorkspaceFileReference( workspaceId: string, fileReference: string, options?: WorkspaceFileLookupOptions ): Promise { + if (options?.includeTestFiles) { + const testName = parseTestFileReference(fileReference) + if (testName !== null) { + const owner = await getLiveWorkflowTestByName(workspaceId, testName) + return owner + ? getWorkspaceFileWithCurrentVersion(workspaceId, owner.bodyFileId, { + includeTestFiles: true, + }) + : null + } + } const includeChatUploads = options?.includeChatUploads === true if (includeChatUploads) { const uploadName = parseChatUploadReference(fileReference) @@ -1682,6 +1788,7 @@ export async function resolveWorkspaceFileReference( if (normalizedReference.startsWith('wf_') || isUuid(normalizedReference)) { const file = await getWorkspaceFileWithCurrentVersion(workspaceId, normalizedReference, { includeChatUploads, + includeTestFiles: options?.includeTestFiles, }) if (file) return file } @@ -1705,6 +1812,7 @@ export async function loadActiveWorkspaceFileContext( const [context] = await db .select({ fileId: workspaceFiles.id, + fileContext: workspaceFiles.context, workspaceId: workspace.id, workspaceOrganizationId: workspace.organizationId, allowPersonalApiKeys: workspace.allowPersonalApiKeys, @@ -1715,7 +1823,7 @@ export async function loadActiveWorkspaceFileContext( .where( and( eq(workspaceFiles.id, fileId), - workspaceFileContextCondition(options?.includeChatUploads), + workspaceFileContextCondition(options), ...(options?.includeDeleted ? [] : [isNull(workspaceFiles.deletedAt)]), isNull(workspace.archivedAt) ) @@ -1736,6 +1844,7 @@ export async function loadWorkspaceFileLifecycleContext( const [context] = await db .select({ fileId: workspaceFiles.id, + fileContext: workspaceFiles.context, workspaceId: workspace.id, workspaceOrganizationId: workspace.organizationId, allowPersonalApiKeys: workspace.allowPersonalApiKeys, @@ -1798,7 +1907,7 @@ export async function getWorkspaceFile( and( eq(workspaceFiles.id, fileId), eq(workspaceFiles.workspaceId, workspaceId), - workspaceFileContextCondition(options?.includeChatUploads), + workspaceFileContextCondition(options), ...(includeDeleted ? [] : [isNull(workspaceFiles.deletedAt)]) ) ) @@ -1835,7 +1944,7 @@ export async function getWorkspaceFileWithCurrentVersion( workspaceFileScopeCondition( workspaceId, options?.includeDeleted ? 'all' : 'active', - options?.includeChatUploads + options ) ) ) @@ -1991,6 +2100,8 @@ export async function updateWorkspaceFileContent( * An omitted policy is classified as unknown rather than inheriting provenance across new bytes. */ secretProvenancePolicy?: WorkspaceFileSecretProvenancePolicy + /** Records what the file's owner derives from these bytes, atomically with the bytes. */ + commitOwner?: (tx: DbOrTx) => Promise } ): Promise { if (options.collabDocState && !options.expectedUpdatedAt) { @@ -1998,7 +2109,9 @@ export async function updateWorkspaceFileContent( } logger.info(`Updating workspace file content: ${fileId} for workspace ${workspaceId}`) - const fileRecord = await getWorkspaceFile(workspaceId, fileId) + const fileRecord = await getWorkspaceFile(workspaceId, fileId, { + includeTestFiles: true, + }) if (!fileRecord) { throw new OrchestrationError('not_found', 'File not found') } @@ -2046,7 +2159,7 @@ export async function updateWorkspaceFileContent( and( eq(workspaceFiles.id, fileId), eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), + contentWritableWorkspaceFileContextCondition, isNull(workspaceFiles.deletedAt) ) ) @@ -2115,7 +2228,7 @@ export async function updateWorkspaceFileContent( and( eq(workspaceFiles.id, fileId), eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), + contentWritableWorkspaceFileContextCondition, isNull(workspaceFiles.deletedAt) ) ) @@ -2123,6 +2236,7 @@ export async function updateWorkspaceFileContent( if (!updatedFile) { throw new OrchestrationError('not_found', 'File not found or could not be updated') } + await options.commitOwner?.(tx) const nextProvenance = await applyWorkspaceFileSecretProvenancePolicyInTx( tx, diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-secret-provenance.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-secret-provenance.ts index 69be5efc158..0fd5ee48452 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-secret-provenance.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-secret-provenance.ts @@ -438,7 +438,7 @@ async function markWorkspaceFileSecretProvenanceTrackedInTx( eq(workspaceFiles.id, fileId), gte(workspaceFiles.contentUpdatedAt, contentUpdatedAt), lt(workspaceFiles.contentUpdatedAt, nextContentMillisecond), - inArray(workspaceFiles.context, ['workspace', 'mothership', 'execution']), + inArray(workspaceFiles.context, ['workspace', 'mothership', 'execution', 'test']), or( isNull(workspaceFiles.secretProvenanceVersion), eq(workspaceFiles.secretProvenanceVersion, 1) diff --git a/apps/sim/lib/uploads/shared/types.ts b/apps/sim/lib/uploads/shared/types.ts index ed67609ef2b..4e636f9bef4 100644 --- a/apps/sim/lib/uploads/shared/types.ts +++ b/apps/sim/lib/uploads/shared/types.ts @@ -69,6 +69,7 @@ export type StorageContext = | 'mothership' | 'execution' | 'workspace' + | 'test' | 'table-import' | 'profile-pictures' | 'og-images' diff --git a/apps/sim/lib/workflow-tests/application/availability.ts b/apps/sim/lib/workflow-tests/application/availability.ts new file mode 100644 index 00000000000..26d9381bd50 --- /dev/null +++ b/apps/sim/lib/workflow-tests/application/availability.ts @@ -0,0 +1,28 @@ +import { defineAuthorizedWorkspaceUseCase, defineWorkspaceOperation } from '@/lib/core/application' +import { isWorkflowTestsEnabled } from '@/lib/workflow-tests/feature-flag' +import { resolveActiveWorkspaceApplicationContext } from '@/lib/workspaces/application/workspace-context' + +/** permission-group-exempt: reports rollout availability only; test operations enforce files.use. */ +const availabilityOperation = defineWorkspaceOperation({ + id: 'workflow_tests.availability', + minimumRole: 'read', + oauthScope: 'api:read', + workspaceApiKey: 'allow', + capability: 'none', + principalKinds: [ + 'session', + 'personal_api_key', + 'workspace_api_key', + 'oauth_access_token', + 'delegated', + ], + delegatedServices: ['copilot'], +}) + +export const readWorkflowTestAvailability = defineAuthorizedWorkspaceUseCase({ + operation: availabilityOperation, + resolveContext: ({ input }: { input: { workspaceId: string } }) => + resolveActiveWorkspaceApplicationContext(input.workspaceId), + authorizationOptions: { delegation: { audience: 'sim:workspaces', isWithinScope: () => true } }, + execute: ({ context }) => isWorkflowTestsEnabled(context.workspaceOrganizationId), +}) diff --git a/apps/sim/lib/workflow-tests/application/operations.ts b/apps/sim/lib/workflow-tests/application/operations.ts new file mode 100644 index 00000000000..09ffaf3ef0c --- /dev/null +++ b/apps/sim/lib/workflow-tests/application/operations.ts @@ -0,0 +1,47 @@ +import { defineWorkspaceOperation } from '@/lib/core/application/workspace-operation' + +/** Workspace API keys are refused: a test run's workflow runs are attributed to the acting user. */ +const policy = { + workspaceApiKey: 'deny', + principalKinds: ['session', 'personal_api_key', 'oauth_access_token', 'delegated'], + delegatedServices: ['copilot'], +} as const + +/** Test files keep the access policy of their file-backed source, like dashboards. */ +export const workflowTestOperations = { + read: defineWorkspaceOperation({ + ...policy, + oauthScope: 'api:read', + capability: 'files.use', + id: 'workflow_tests.read', + minimumRole: 'read', + }), + create: defineWorkspaceOperation({ + ...policy, + oauthScope: 'api:write', + capability: 'files.use', + id: 'workflow_tests.create', + minimumRole: 'write', + }), + update: defineWorkspaceOperation({ + ...policy, + oauthScope: 'api:write', + capability: 'files.use', + id: 'workflow_tests.update', + minimumRole: 'write', + }), + run: defineWorkspaceOperation({ + ...policy, + oauthScope: 'api:write', + capability: 'files.use', + id: 'workflow_tests.run', + minimumRole: 'write', + }), + delete: defineWorkspaceOperation({ + ...policy, + oauthScope: 'api:write', + capability: 'files.use', + id: 'workflow_tests.delete', + minimumRole: 'write', + }), +} as const diff --git a/apps/sim/lib/workflow-tests/application/run-tests.ts b/apps/sim/lib/workflow-tests/application/run-tests.ts new file mode 100644 index 00000000000..d5c1b3de382 --- /dev/null +++ b/apps/sim/lib/workflow-tests/application/run-tests.ts @@ -0,0 +1,146 @@ +import { + type Principal, + resolvePrincipalSubjectUserId, + toPrincipalActor, +} from '@sim/auth/principal' +import { createLogger } from '@sim/logger' +import { getErrorMessage } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { defineAuthorizedWorkspaceUseCase } from '@/lib/core/application' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { + fetchWorkspaceFileBuffer, + getWorkspaceFile, +} from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { testSourceHash } from '@/lib/workflow-tests/definition' +import { requireWorkflowTestsEnabled } from '@/lib/workflow-tests/feature-flag' +import { testFilePath } from '@/lib/workflow-tests/paths' +import { + completeWorkflowTestRun, + failWorkflowTestRun, + insertWorkflowTestRuns, + listLiveWorkflowTests, + markWorkflowTestRunStarted, + readRanAgainst, + recordWorkflowTestProgress, + type WorkflowTestRow, +} from '@/lib/workflow-tests/repository' +import { runWorkflowTestFile } from '@/lib/workflow-tests/run-test-file' +import { MAX_TEST_SOURCE_BYTES } from '@/lib/workflow-tests/source-write' +import type { WorkflowTestVersion } from '@/lib/workflows/application/run-workflow-for-test' +import { resolveActiveWorkspaceApplicationContext } from '@/lib/workspaces/application/workspace-context' + +const logger = createLogger('WorkflowTestRuns') + +const authorizationOptions = { + delegation: { audience: 'sim:workspaces', isWithinScope: () => true }, +} as const + +async function executeTestRun(params: { + runId: string + test: WorkflowTestRow + principal: Principal + version: WorkflowTestVersion + only: string[] | null +}): Promise { + const { runId, test, principal, version } = params + let sourceHash: string | null = null + try { + await markWorkflowTestRunStarted(runId) + const file = await getWorkspaceFile(test.workspaceId, test.bodyFileId, { + includeTestFiles: true, + }) + if (!file) throw new Error(`${testFilePath(test.name)} has no source file`) + const buffer = await fetchWorkspaceFileBuffer(file, { maxBytes: MAX_TEST_SOURCE_BYTES }) + const source = buffer.toString('utf-8') + sourceHash = testSourceHash(source) + const { report, enteredWorkflowIds } = await runWorkflowTestFile({ + principal, + workspaceId: test.workspaceId, + source, + filePath: testFilePath(test.name), + version, + only: params.only, + onProgress: (path, status) => recordWorkflowTestProgress(runId, path.join(' > '), status), + }) + const executionIds = report.tests.flatMap((result) => + result.executions.map((execution) => execution.executionId) + ) + const ranAgainst = await readRanAgainst({ + executionIds, + enteredWorkflowIds, + workspaceId: test.workspaceId, + version, + }) + await completeWorkflowTestRun(runId, report, sourceHash, ranAgainst) + } catch (error) { + logger.warn('Workflow test run failed', { + runId, + testId: test.id, + error: getErrorMessage(error), + }) + await failWorkflowTestRun(runId, getErrorMessage(error), sourceHash) + } +} + +export interface RunWorkflowTestsInput { + workspaceId: string + version: WorkflowTestVersion + /** Test file names to run; omitted runs every test file in the workspace. */ + names?: string[] + /** With exactly one name: the `describe > it` paths to run inside it. */ + only?: string[] +} + +/** + * Starts one run per test file and returns their ids at once; the runs finish in this + * process and record their reports on `workflow_test_run`. Files run one after another so + * a large suite does not occupy every sandbox slot. + */ +export const startWorkflowTestRuns = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.run, + resolveContext: ({ input }: { input: RunWorkflowTestsInput }) => + resolveActiveWorkspaceApplicationContext(input.workspaceId), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ principal, input, context }) { + if (input.only && input.names?.length !== 1) { + throw new OrchestrationError('validation', 'only needs exactly one test file name') + } + const all = await listLiveWorkflowTests(context.workspaceId) + const tests = input.names + ? input.names.map((name) => { + const test = all.find((row) => row.name === name) + if (!test) throw new OrchestrationError('not_found', `No test file ${testFilePath(name)}`) + return test + }) + : all + if (tests.length === 0) throw new OrchestrationError('validation', 'There are no tests to run') + + const actor = toPrincipalActor(principal) + const runs = tests.map((test) => ({ runId: generateId(), test })) + await insertWorkflowTestRuns( + runs.map(({ runId, test }) => ({ + id: runId, + testId: test.id, + workspaceId: context.workspaceId, + version: input.version, + triggeredByActor: actor, + triggeredByUserId: resolvePrincipalSubjectUserId(principal) ?? null, + })) + ) + void (async () => { + for (const { runId, test } of runs) { + await executeTestRun({ + runId, + test, + principal, + version: input.version, + only: input.only ?? null, + }) + } + })() + return { runs: runs.map(({ runId, test }) => ({ runId, name: test.name })) } + }, +}) diff --git a/apps/sim/lib/workflow-tests/application/tests.ts b/apps/sim/lib/workflow-tests/application/tests.ts new file mode 100644 index 00000000000..038ecabb4a2 --- /dev/null +++ b/apps/sim/lib/workflow-tests/application/tests.ts @@ -0,0 +1,397 @@ +import { requirePrincipalSubjectUserId } from '@sim/auth/principal' +import { getPostgresErrorCode } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { defineAuthorizedWorkspaceUseCase } from '@/lib/core/application' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { createOwnedWorkspaceFile } from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { workflowTestOperations } from '@/lib/workflow-tests/application/operations' +import { starterTestSource, testSourceHash } from '@/lib/workflow-tests/definition' +import { requireWorkflowTestsEnabled } from '@/lib/workflow-tests/feature-flag' +import { assertTestName, testFilePath } from '@/lib/workflow-tests/paths' +import type { TestCaseStatus, TestReport } from '@/lib/workflow-tests/protocol' +import { + type DeploymentFacts, + getLatestWorkflowTestRun, + getLiveWorkflowTestByName, + getWorkflowTestRunById, + insertWorkflowTestInTx, + listLiveWorkflowTests, + listRecentWorkflowTestRuns, + listWorkflowTestRunsById, + type RanAgainstEntry, + RECENT_RUNS_PER_TEST, + readDeploymentFacts, + softDeleteWorkflowTest, + updateWorkflowTestMetadata, + type WorkflowTestCases, + type WorkflowTestRow, + type WorkflowTestRunRow, +} from '@/lib/workflow-tests/repository' +import type { WorkflowTestVersion } from '@/lib/workflows/application/run-workflow-for-test' +import { resolveActiveWorkspaceApplicationContext } from '@/lib/workspaces/application/workspace-context' + +const authorizationOptions = { + delegation: { audience: 'sim:workspaces', isWithinScope: () => true }, +} as const + +/** A run that has said nothing for this long died with its process. */ +const ABANDONED_RUN_MS = 30 * 60_000 + +type WorkflowTestStatus = 'passing' | 'failing' | 'error' | 'running' | 'changed' | 'not_run' + +export interface WorkflowTestRunSummary { + id: string + status: 'running' | 'passed' | 'failed' | 'error' + passed: number + failed: number + skipped: number + error: string | null + triggeredByUserId: string | null + version: WorkflowTestVersion + /** The test file changed after this run. */ + sourceChanged: boolean + ranAgainst: WorkflowTestRanAgainst[] + /** Whether the run still describes the test: same source, and no workflow redeployed since. */ + current: boolean + startedAt: string + completedAt: string | null +} + +interface WorkflowTestRanAgainst { + workflowId: string + /** `null` when the workflow has since been deleted. */ + name: string | null + /** `null` for a draft run, or a deployment since removed. */ + version: number | null + draft: boolean + liveVersion: number | null + /** The workflow was redeployed, its draft edited, or it was deleted after this run. */ + stale: boolean + /** A `runWorkflow()` execution of it, whose snapshot is the workflow as it ran; null for a child. */ + executionId: string | null +} + +type RunRow = Omit + +/** Reads what every run's workflows look like now, in three queries for any number of runs. */ +async function loadDeploymentFacts(rows: RunRow[]): Promise { + const entries = rows.flatMap(ranAgainstEntries) + return readDeploymentFacts( + [...new Set(entries.map((entry) => entry.workflowId))], + [...new Set(entries.flatMap((entry) => entry.deploymentVersionId ?? []))] + ) +} + +function ranAgainstEntries(row: RunRow): RanAgainstEntry[] { + return (row.ranAgainst ?? []) as RanAgainstEntry[] +} + +function presentRanAgainst(row: RunRow, facts: DeploymentFacts): WorkflowTestRanAgainst[] { + return ranAgainstEntries(row).map((entry) => { + const name = facts.names.get(entry.workflowId) ?? null + const live = facts.active.get(entry.workflowId) + const deploymentId = entry.deploymentVersionId + return { + workflowId: entry.workflowId, + name, + version: deploymentId === null ? null : (facts.versions.get(deploymentId) ?? null), + draft: deploymentId === null, + liveVersion: live?.version ?? null, + stale: + name === null || + (deploymentId === null ? draftEdited(entry, facts) : live?.id !== deploymentId), + executionId: entry.executionId, + } + }) +} + +function draftEdited(entry: RanAgainstEntry, facts: DeploymentFacts): boolean { + const updatedAt = facts.updatedAt.get(entry.workflowId) + return ( + entry.draftUpdatedAt === null || + updatedAt === undefined || + updatedAt.getTime() > Date.parse(entry.draftUpdatedAt) + ) +} + +function runStatus(row: Omit): WorkflowTestRunSummary['status'] { + if (row.status !== 'running') return row.status as WorkflowTestRunSummary['status'] + return Date.now() - row.startedAt.getTime() > ABANDONED_RUN_MS ? 'error' : 'running' +} + +function presentRun( + row: RunRow, + testSourceHash: string, + facts: DeploymentFacts +): WorkflowTestRunSummary { + const status = runStatus(row) + const sourceChanged = row.sourceHash !== testSourceHash + const ranAgainst = presentRanAgainst(row, facts) + return { + id: row.id, + status, + passed: row.passed, + failed: row.failed, + skipped: row.skipped, + error: + status === 'error' && row.status === 'running' ? 'The run stopped responding' : row.error, + triggeredByUserId: row.triggeredByUserId, + version: row.version as WorkflowTestVersion, + sourceChanged, + ranAgainst, + current: !sourceChanged && !ranAgainst.some((workflow) => workflow.stale), + startedAt: row.startedAt.toISOString(), + completedAt: row.completedAt?.toISOString() ?? null, + } +} + +function testStatus(latest: WorkflowTestRunSummary | undefined): WorkflowTestStatus { + if (!latest) return 'not_run' + if (latest.status !== 'running' && !latest.current) return 'changed' + if (latest.status === 'passed') return 'passing' + if (latest.status === 'failed') return 'failing' + return latest.status +} + +function presentTest(row: WorkflowTestRow, runs: WorkflowTestRunSummary[]) { + const cases = row.cases as WorkflowTestCases + return { + id: row.id, + name: row.name, + title: row.title, + description: row.description, + path: testFilePath(row.name), + fileId: row.bodyFileId, + caseCount: cases.length, + createdByUserId: row.createdByUserId, + createdAt: row.createdAt.toISOString(), + status: testStatus(runs[0]), + recentRuns: runs, + updatedAt: row.updatedAt.toISOString(), + } +} + +export const listWorkflowTests = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.read, + resolveContext: ({ input }: { input: { workspaceId: string; version?: WorkflowTestVersion } }) => + resolveActiveWorkspaceApplicationContext(input.workspaceId), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ input, context }) { + const rows = await listLiveWorkflowTests(context.workspaceId) + const runs = await listRecentWorkflowTestRuns( + rows.map((row) => row.id), + input.version ?? null, + RECENT_RUNS_PER_TEST + ) + const facts = await loadDeploymentFacts(runs) + const sourceHashByTest = new Map(rows.map((row) => [row.id, row.sourceHash])) + const runsByTest = new Map() + for (const run of runs) { + const sourceHash = sourceHashByTest.get(run.testId) + if (sourceHash === undefined) throw new Error(`Run ${run.id} belongs to no listed test`) + const list = runsByTest.get(run.testId) ?? [] + list.push(presentRun(run, sourceHash, facts)) + runsByTest.set(run.testId, list) + } + return { tests: rows.map((row) => presentTest(row, runsByTest.get(row.id) ?? [])) } + }, +}) + +interface WorkflowTestInput { + workspaceId: string + name: string +} + +async function resolveTestContext({ input }: { input: WorkflowTestInput }) { + const context = await resolveActiveWorkspaceApplicationContext(input.workspaceId) + const test = await getLiveWorkflowTestByName(context.workspaceId, input.name) + if (!test) throw new OrchestrationError('not_found', `No test file ${testFilePath(input.name)}`) + return { ...context, test } +} + +export const getWorkflowTestDetail = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.read, + resolveContext: ({ input }: { input: WorkflowTestInput & { version?: WorkflowTestVersion } }) => + resolveTestContext({ input }), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ input, context }) { + const [latest, recent] = await Promise.all([ + getLatestWorkflowTestRun(context.test.id, input.version ?? null), + listRecentWorkflowTestRuns([context.test.id], input.version ?? null, 20), + ]) + const facts = await loadDeploymentFacts(latest ? [latest, ...recent] : recent) + const history = recent.map((run) => presentRun(run, context.test.sourceHash, facts)) + return { + test: { + ...presentTest(context.test, history.slice(0, RECENT_RUNS_PER_TEST)), + cases: context.test.cases as WorkflowTestCases, + }, + latestRun: latest ? presentRunDetail(latest, context.test.sourceHash, facts) : null, + history, + } + }, +}) + +function presentRunDetail(row: WorkflowTestRunRow, testSourceHash: string, facts: DeploymentFacts) { + return { + ...presentRun(row, testSourceHash, facts), + report: row.report as TestReport | null, + progress: row.progress as Record | null, + } +} + +/** One run of a test with its report, for reading an earlier run's results. */ +export const getWorkflowTestRunDetail = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.read, + resolveContext: ({ input }: { input: WorkflowTestInput & { runId: string } }) => + resolveTestContext({ input }), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ input, context }) { + const run = await getWorkflowTestRunById(context.test.id, input.runId) + if (!run) throw new OrchestrationError('not_found', 'That run was not found') + const facts = await loadDeploymentFacts([run]) + return { run: presentRunDetail(run, context.test.sourceHash, facts) } + }, +}) + +const MAX_TITLE_LENGTH = 200 +const MAX_DESCRIPTION_LENGTH = 2000 + +function cleanTitle(title: string): string { + const trimmed = title.trim() + if (!trimmed || trimmed.length > MAX_TITLE_LENGTH) { + throw new OrchestrationError( + 'validation', + `A test title is 1 to ${MAX_TITLE_LENGTH} characters: the concern the file tests` + ) + } + return trimmed +} + +function cleanDescription(description: string | null | undefined): string | null { + const trimmed = description?.trim() ?? '' + if (trimmed.length > MAX_DESCRIPTION_LENGTH) { + throw new OrchestrationError( + 'validation', + `A test description is at most ${MAX_DESCRIPTION_LENGTH} characters` + ) + } + return trimmed || null +} + +export interface CreateWorkflowTestInput { + workspaceId: string + name: string + title: string + description?: string +} + +/** + * Creates a test: its metadata, and `tests/.test.js` holding an empty describe. The + * cases are written into that file afterwards with the file tools, and every write is checked. + */ +export const createWorkflowTest = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.create, + resolveContext: ({ input }: { input: CreateWorkflowTestInput }) => + resolveActiveWorkspaceApplicationContext(input.workspaceId), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ principal, input, context }) { + assertTestName(input.name) + const title = cleanTitle(input.title) + const description = cleanDescription(input.description) + const path = testFilePath(input.name) + if (await getLiveWorkflowTestByName(context.workspaceId, input.name)) { + throw new OrchestrationError('conflict', `${path} already exists`) + } + const userId = requirePrincipalSubjectUserId(principal) + const source = starterTestSource(title) + try { + const { owner } = await createOwnedWorkspaceFile({ + context: 'test', + workspaceId: context.workspaceId, + userId, + content: source, + contentType: 'text/javascript', + fileName: () => `${input.name}.test.js`, + insertOwner: (tx, bodyFileId) => + insertWorkflowTestInTx(tx, { + id: generateId(), + workspaceId: context.workspaceId, + name: input.name, + title, + description, + bodyFileId, + cases: [], + sourceHash: testSourceHash(source), + createdByUserId: userId, + }), + }) + return { test: presentTest(owner, []) } + } catch (error) { + if (getPostgresErrorCode(error) === '23505') { + throw new OrchestrationError('conflict', `${path} already exists`) + } + throw error + } + }, +}) + +export interface UpdateWorkflowTestInput extends WorkflowTestInput { + title?: string + description?: string | null +} + +/** Changes a test's metadata; its cases change by editing the file. */ +export const updateWorkflowTest = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.update, + resolveContext: ({ input }: { input: UpdateWorkflowTestInput }) => resolveTestContext({ input }), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ input, context }) { + if (input.title === undefined && input.description === undefined) { + throw new OrchestrationError('validation', 'Pass a title or a description to change') + } + const row = await updateWorkflowTestMetadata(context.test.id, { + ...(input.title !== undefined ? { title: cleanTitle(input.title) } : {}), + ...(input.description !== undefined + ? { description: cleanDescription(input.description) } + : {}), + }) + return { test: presentTest(row, []) } + }, +}) + +export const deleteWorkflowTest = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.delete, + resolveContext: resolveTestContext, + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ context }) { + await softDeleteWorkflowTest(context.test) + return { deleted: true } + }, +}) + +/** Runs by id with their reports, for a caller waiting on runs it started. */ +export const readWorkflowTestRuns = defineAuthorizedWorkspaceUseCase({ + operation: workflowTestOperations.read, + resolveContext: ({ input }: { input: { workspaceId: string; runIds: string[] } }) => + resolveActiveWorkspaceApplicationContext(input.workspaceId), + authorizationOptions, + authorizeResource: ({ context }) => requireWorkflowTestsEnabled(context.workspaceOrganizationId), + async execute({ input, context }) { + const rows = await listWorkflowTestRunsById(input.runIds, context.workspaceId) + const facts = await loadDeploymentFacts(rows) + return { + runs: rows.map((row) => ({ + ...presentRun(row, row.testSourceHash, facts), + name: row.testName, + report: row.report as TestReport | null, + })), + } + }, +}) diff --git a/apps/sim/lib/workflow-tests/brokers.ts b/apps/sim/lib/workflow-tests/brokers.ts new file mode 100644 index 00000000000..1e877c04845 --- /dev/null +++ b/apps/sim/lib/workflow-tests/brokers.ts @@ -0,0 +1,80 @@ +import type { SandboxBroker, SandboxBrokerContext } from '@/lib/execution/sandbox/types' +import { + testCancelArgsSchema, + testJudgeArgsSchema, + testNextArgsSchema, + testProgressArgsSchema, + testReplyArgsSchema, + testStartArgsSchema, +} from '@/lib/workflow-tests/protocol' +import type { WorkflowTestSession } from '@/lib/workflow-tests/session' +import { requireWorkflowTestSession } from '@/lib/workflow-tests/session-registry' + +/** The session this sandbox run belongs to, scoped to the broker context's workspace. */ +function sessionFor(ctx: SandboxBrokerContext): WorkflowTestSession { + const session = requireWorkflowTestSession(ctx.requestId) + if (session.config.workspaceId !== ctx.workspaceId) { + throw new Error('This workflow test run belongs to another workspace') + } + return session +} + +const testPlan: SandboxBroker = { + name: 'testPlan', + async handle(ctx) { + return { only: sessionFor(ctx).config.only } + }, +} + +const testStart: SandboxBroker = { + name: 'testStart', + handle: (ctx, args) => sessionFor(ctx).start(testStartArgsSchema.parse(args)), +} + +const testNext: SandboxBroker = { + name: 'testNext', + handle: (ctx, args) => sessionFor(ctx).next(testNextArgsSchema.parse(args).runId), +} + +const testReply: SandboxBroker = { + name: 'testReply', + handle(ctx, args) { + const reply = testReplyArgsSchema.parse(args) + return sessionFor(ctx).reply( + reply.runId, + reply.callId, + 'error' in reply ? { error: reply.error } : { output: reply.output } + ) + }, +} + +const testCancel: SandboxBroker = { + name: 'testCancel', + async handle(ctx, args) { + sessionFor(ctx).cancel(testCancelArgsSchema.parse(args).runIds) + return null + }, +} + +const testJudge: SandboxBroker = { + name: 'testJudge', + handle: (ctx, args) => sessionFor(ctx).judge(testJudgeArgsSchema.parse(args)), +} + +const testProgress: SandboxBroker = { + name: 'testProgress', + async handle(ctx, args) { + await sessionFor(ctx).progress(testProgressArgsSchema.parse(args)) + return null + }, +} + +export const workflowTestBrokers: ReadonlyArray = [ + testPlan, + testStart, + testNext, + testReply, + testCancel, + testJudge, + testProgress, +] diff --git a/apps/sim/lib/workflow-tests/collect.ts b/apps/sim/lib/workflow-tests/collect.ts new file mode 100644 index 00000000000..a35741c7785 --- /dev/null +++ b/apps/sim/lib/workflow-tests/collect.ts @@ -0,0 +1,16 @@ +import { runSandboxTask } from '@/lib/execution/sandbox/run-task' +import { type TestCollection, testCollectionSchema } from '@/lib/workflow-tests/protocol' + +/** Evaluates a test file without running it: its top-level blocks and every test name. */ +export async function collectWorkflowTestFile(input: { + workspaceId: string + source: string + filePath: string +}): Promise { + const bytes = await runSandboxTask( + 'workflow-test-collect', + { workspaceId: input.workspaceId, code: input.source, codeFilename: input.filePath }, + { ownerKey: `workspace:${input.workspaceId}` } + ) + return testCollectionSchema.parse(JSON.parse(Buffer.from(bytes).toString('utf-8'))) +} diff --git a/apps/sim/lib/workflow-tests/definition.ts b/apps/sim/lib/workflow-tests/definition.ts new file mode 100644 index 00000000000..4996d97ce12 --- /dev/null +++ b/apps/sim/lib/workflow-tests/definition.ts @@ -0,0 +1,68 @@ +import { sha256Hex } from '@sim/security/hash' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { SandboxUserCodeError } from '@/lib/execution/sandbox/run-task' +import { collectWorkflowTestFile } from '@/lib/workflow-tests/collect' +import { testFilePath } from '@/lib/workflow-tests/paths' +import type { WorkflowTestCases } from '@/lib/workflow-tests/repository' + +/** + * Evaluates a test file's source and returns the cases it declares. A file is one concern: + * exactly one top-level `describe`, holding every test. Anything else is refused with a message + * the writer can act on, so an invalid file is never saved. + */ +export async function readWorkflowTestCases(input: { + workspaceId: string + name: string + source: string +}): Promise { + const filePath = testFilePath(input.name) + let collection: Awaited> + try { + collection = await collectWorkflowTestFile({ + workspaceId: input.workspaceId, + source: input.source, + filePath, + }) + } catch (error) { + if (error instanceof SandboxUserCodeError) { + throw new OrchestrationError('validation', `${filePath} does not load: ${error.message}`) + } + throw error + } + + const [top, ...rest] = collection.topLevel + if (!top || top.kind !== 'suite' || rest.length > 0) { + throw new OrchestrationError( + 'validation', + `${filePath} must contain exactly one top-level describe(...) with every test inside it` + ) + } + const seen = new Set() + for (const testCase of collection.tests) { + const key = testCase.path.join(' > ') + if (seen.has(key)) { + throw new OrchestrationError( + 'validation', + `${filePath} has two tests named "${key}"; give each test its own name` + ) + } + seen.add(key) + } + return collection.tests +} + +/** Identifies the exact source a test's cases and runs were read from. */ +export function testSourceHash(source: string): string { + return sha256Hex(source) +} + +/** The source `tests create` starts a test file with: one empty describe, ready for cases. */ +export function starterTestSource(title: string): string { + return [ + "import { describe, expect, it, vi } from 'vitest'", + "import { mockBlock, mockTool, runWorkflow, spyOnBlock } from 'sim:test'", + '', + `describe(${JSON.stringify(title)}, () => {})`, + '', + ].join('\n') +} diff --git a/apps/sim/lib/workflow-tests/feature-flag.ts b/apps/sim/lib/workflow-tests/feature-flag.ts new file mode 100644 index 00000000000..ff728f679f0 --- /dev/null +++ b/apps/sim/lib/workflow-tests/feature-flag.ts @@ -0,0 +1,13 @@ +import { isFeatureEnabled } from '@/lib/core/config/feature-flags' +import { OrchestrationError } from '@/lib/core/orchestration/types' + +/** Test rollout follows the canonical organization; personal workspaces use the global switch. */ +export function isWorkflowTestsEnabled(orgId?: string | null): Promise { + return isFeatureEnabled('workflow-tests', orgId ? { orgId } : {}) +} + +export async function requireWorkflowTestsEnabled(orgId?: string | null): Promise { + if (!(await isWorkflowTestsEnabled(orgId))) { + throw new OrchestrationError('forbidden', 'Workflow tests are not enabled') + } +} diff --git a/apps/sim/lib/workflow-tests/judge.ts b/apps/sim/lib/workflow-tests/judge.ts new file mode 100644 index 00000000000..a36426760f9 --- /dev/null +++ b/apps/sim/lib/workflow-tests/judge.ts @@ -0,0 +1,106 @@ +import { createLogger } from '@sim/logger' +import { generateShortId } from '@sim/utils/id' +import { z } from 'zod' +import { + checkAttributedUsageLimits, + resolveBillingAttribution, + toBillingContext, +} from '@/lib/billing/core/billing-attribution' +import { recordUsage } from '@/lib/billing/core/usage-log' +import { checkAndBillPayerOverageThreshold } from '@/lib/billing/threshold-billing' +import { validateModelProvider } from '@/ee/access-control/utils/permission-check' +import { EVALUATOR } from '@/executor/constants' +import { executeProviderRequest } from '@/providers' +import { resolveProxiedModelCost } from '@/providers/cost-policy' +import { getProviderFromModel } from '@/providers/utils' + +const logger = createLogger('WorkflowTestJudge') + +const JUDGE_MODEL = EVALUATOR.DEFAULT_MODEL + +const JUDGE_SYSTEM_PROMPT = [ + 'You check one output against one requirement for an automated test.', + 'Judge only what the requirement states, and pass only if every part of it holds.', + 'In reason, quote the part of the output your verdict rests on, in one or two sentences.', +].join(' ') + +const verdictSchema = z.object({ pass: z.boolean(), reason: z.string().trim().min(1) }) +export type JudgeVerdict = z.infer + +export interface JudgeRubricInput { + workspaceId: string + actorUserId: string + value: string + rubric: string + abortSignal: AbortSignal +} + +/** One pass/fail model call for `toMatchRubric`, billed to the workspace like a wand call. */ +export async function judgeRubric(input: JudgeRubricInput): Promise { + const billingAttribution = await resolveBillingAttribution({ + actorUserId: input.actorUserId, + workspaceId: input.workspaceId, + }) + const usage = await checkAttributedUsageLimits(billingAttribution) + if (usage.isExceeded) throw new Error(usage.message || 'Usage limit exceeded') + await validateModelProvider(input.actorUserId, input.workspaceId, JUDGE_MODEL) + + const response = await executeProviderRequest(getProviderFromModel(JUDGE_MODEL), { + model: JUDGE_MODEL, + systemPrompt: JUDGE_SYSTEM_PROMPT, + messages: [ + { + role: 'user', + content: `Requirement:\n${input.rubric}\n\nOutput:\n${input.value}`, + }, + ], + responseFormat: { + name: 'test_verdict', + schema: { + type: 'object', + properties: { pass: { type: 'boolean' }, reason: { type: 'string' } }, + required: ['pass', 'reason'], + additionalProperties: false, + }, + strict: true, + }, + temperature: 0, + workspaceId: input.workspaceId, + abortSignal: input.abortSignal, + }) + if (!('content' in response) || typeof response.content !== 'string') { + throw new Error('The judge did not return a verdict') + } + + const cost = resolveProxiedModelCost(response.cost) + await recordUsage({ + userId: billingAttribution.actorUserId, + workspaceId: input.workspaceId, + ...toBillingContext(billingAttribution), + entries: [ + { + category: 'model', + source: 'workflow-test', + description: JUDGE_MODEL, + cost: cost.total, + sourceReference: `workflow-test-judge:${generateShortId()}`, + metadata: { + inputTokens: response.tokens?.input ?? 0, + outputTokens: response.tokens?.output ?? 0, + }, + }, + ], + }) + await checkAndBillPayerOverageThreshold(billingAttribution.billingEntity) + + let parsed: unknown + try { + parsed = JSON.parse(response.content) + } catch { + logger.warn('Judge returned non-JSON content', { length: response.content.length }) + throw new Error('The judge returned an unreadable verdict') + } + const verdict = verdictSchema.safeParse(parsed) + if (!verdict.success) throw new Error('The judge returned an unreadable verdict') + return verdict.data +} diff --git a/apps/sim/lib/workflow-tests/mock-channel.ts b/apps/sim/lib/workflow-tests/mock-channel.ts new file mode 100644 index 00000000000..704a6a3912c --- /dev/null +++ b/apps/sim/lib/workflow-tests/mock-channel.ts @@ -0,0 +1,120 @@ +import { toError } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import type { + ExecutionTestHooks, + MockedBlockCall, + MockedToolCall, +} from '@/executor/execution/types' + +type PendingMockCall = + | { kind: 'block'; call: MockedBlockCall } + | { kind: 'tool'; call: MockedToolCall } + +/** A mocked block or Agent tool call waiting for the test's answer. */ +export type ParkedMockCall = PendingMockCall & { callId: string } + +type MockReply = { output: Record } | { error: string } + +/** + * The server half of a test run's mocks. The executor parks each mocked block and mocked Agent + * tool call on `hooks`; the test sandbox pulls parked calls with `nextCall` and settles them + * with `reply`. + */ +export interface MockChannel { + readonly hooks: ExecutionTestHooks + /** The next parked call, in arrival order. */ + nextCall(): Promise + reply(callId: string, reply: MockReply): void + /** Rejects every parked call and waiter; later calls reject immediately. */ + close(reason: Error): void +} + +interface ParkedEntry { + resolve: (output: Record) => void + reject: (error: Error) => void + detach: () => void +} + +interface Waiter { + resolve: (call: ParkedMockCall) => void + reject: (error: Error) => void +} + +/** The test's side of the hooks: which blocks and tools it answers or records. */ +export type MockMatcher = Pick< + ExecutionTestHooks, + 'enterWorkflow' | 'mocksBlock' | 'mocksTool' | 'spiesBlock' | 'recordSpy' +> + +export function createMockChannel(matcher: MockMatcher): MockChannel { + const parked = new Map() + const undelivered: ParkedMockCall[] = [] + const waiters: Waiter[] = [] + let closedWith: Error | undefined + + const deliver = (call: ParkedMockCall) => { + const waiter = waiters.shift() + if (waiter) waiter.resolve(call) + else undelivered.push(call) + } + + const settle = (callId: string): ParkedEntry => { + const entry = parked.get(callId) + if (!entry) throw new Error(`No parked mock call "${callId}"`) + parked.delete(callId) + entry.detach() + const queued = undelivered.findIndex((call) => call.callId === callId) + if (queued !== -1) undelivered.splice(queued, 1) + return entry + } + + const park = ( + pending: PendingMockCall, + abortSignal?: AbortSignal + ): Promise> => { + if (closedWith) return Promise.reject(closedWith) + if (abortSignal?.aborted) return Promise.reject(toError(abortSignal.reason)) + const callId = generateId() + return new Promise((resolve, reject) => { + const onAbort = () => settle(callId).reject(toError(abortSignal?.reason)) + abortSignal?.addEventListener('abort', onAbort, { once: true }) + parked.set(callId, { + resolve, + reject, + detach: () => abortSignal?.removeEventListener('abort', onAbort), + }) + deliver({ callId, ...pending }) + }) + } + + const hooks: ExecutionTestHooks = { + enterWorkflow: (workflow) => matcher.enterWorkflow(workflow), + mocksBlock: (blockId) => matcher.mocksBlock(blockId), + resolveMock: (call, abortSignal) => park({ kind: 'block', call }, abortSignal), + mocksTool: (blockId, toolId) => matcher.mocksTool(blockId, toolId), + resolveToolMock: (call, abortSignal) => park({ kind: 'tool', call }, abortSignal), + spiesBlock: (blockId) => matcher.spiesBlock(blockId), + recordSpy: (call) => matcher.recordSpy(call), + } + + return { + hooks, + nextCall() { + if (closedWith) return Promise.reject(closedWith) + const queued = undelivered.shift() + if (queued) return Promise.resolve(queued) + return new Promise((resolve, reject) => waiters.push({ resolve, reject })) + }, + reply(callId, reply) { + const entry = settle(callId) + if ('error' in reply) entry.reject(new Error(reply.error)) + else entry.resolve(reply.output) + }, + close(reason) { + if (closedWith) return + closedWith = reason + for (const callId of [...parked.keys()]) settle(callId).reject(reason) + for (const waiter of waiters.splice(0)) waiter.reject(reason) + }, + } +} diff --git a/apps/sim/lib/workflow-tests/paths.ts b/apps/sim/lib/workflow-tests/paths.ts new file mode 100644 index 00000000000..c8d2f8ba881 --- /dev/null +++ b/apps/sim/lib/workflow-tests/paths.ts @@ -0,0 +1,30 @@ +import { TEST_NAME_PATTERN } from '@/lib/api/contracts/mothership-tests' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { decodeVfsPathSegments } from '@/lib/mothership/vfs/path-utils' + +/** Test files live flat in this namespace as `tests/.test.js`. */ +const TEST_FILE_PREFIX = 'tests/' +export const TEST_FILE_SUFFIX = '.test.js' + +export function testFilePath(name: string): string { + return `${TEST_FILE_PREFIX}${name}${TEST_FILE_SUFFIX}` +} + +/** The test name in a `tests/.test.js` reference; null for any other reference. */ +export function parseTestFileReference(reference: string): string | null { + const trimmed = reference.trim().replace(/^\/+/, '') + if (!trimmed.startsWith(TEST_FILE_PREFIX)) return null + const segments = decodeVfsPathSegments(trimmed) + if (segments.length !== 2 || !segments[1].endsWith(TEST_FILE_SUFFIX)) return null + return segments[1].slice(0, -TEST_FILE_SUFFIX.length) +} + +/** Throws a message that tells the writer what a valid test file path looks like. */ +export function assertTestName(name: string): void { + if (!TEST_NAME_PATTERN.test(name)) { + throw new OrchestrationError( + 'validation', + `"${testFilePath(name)}" is not a valid test file. Use tests/.test.js with a lowercase name of letters, numbers, and dashes other than "run", like tests/billing-routing.test.js` + ) + } +} diff --git a/apps/sim/lib/workflow-tests/protocol.ts b/apps/sim/lib/workflow-tests/protocol.ts new file mode 100644 index 00000000000..c8871149726 --- /dev/null +++ b/apps/sim/lib/workflow-tests/protocol.ts @@ -0,0 +1,135 @@ +import { z } from 'zod' + +/** + * The wire between the workflow-test harness (inside the isolate) and the host brokers. + * Everything the isolate sends is untrusted test-author code, so every payload is parsed. + */ + +const nameSchema = z.string().trim().min(1).max(200) +const MAX_TARGETS = 200 + +const keySchema = z.string().min(1).max(500) + +const testTargetSchema = z.discriminatedUnion('kind', [ + z.object({ + key: keySchema, + kind: z.enum(['mock', 'spy']), + workflow: nameSchema.nullable(), + block: nameSchema, + }), + z.object({ + key: keySchema, + kind: z.literal('tool'), + workflow: nameSchema.nullable(), + /** The calling Agent block; null answers the tool for every Agent. */ + block: nameSchema.nullable(), + /** A built-in tool id, an MCP tool's name when `mcpServer` is set, or a custom tool's title. */ + tool: nameSchema, + /** The MCP server, by name, so a test survives the server being re-added or forked. */ + mcpServer: nameSchema.nullable(), + customTool: z.boolean(), + }), +]) +export type TestTarget = z.infer + +export const testStartArgsSchema = z.object({ + workflow: nameSchema, + /** The trigger block that starts the run, by name; needed when the workflow has several. */ + trigger: nameSchema.nullable(), + input: z.unknown(), + targets: z.array(testTargetSchema).max(MAX_TARGETS), +}) + +export const testReplyArgsSchema = z.union([ + z.object({ runId: z.string().min(1), callId: z.string().min(1), output: z.unknown() }), + z.object({ runId: z.string().min(1), callId: z.string().min(1), error: z.string().max(10_000) }), +]) + +export const testNextArgsSchema = z.object({ runId: z.string().min(1) }) + +export const testCaseStatusSchema = z.enum(['running', 'pass', 'fail', 'skip']) +export type TestCaseStatus = z.infer + +export const testProgressArgsSchema = z.object({ + path: z.array(z.string().min(1).max(500)).min(1).max(20), + status: testCaseStatusSchema, +}) +export const testCancelArgsSchema = z.object({ runIds: z.array(z.string().min(1)).max(1000) }) +export const testJudgeArgsSchema = z.object({ + value: z.string().max(200_000), + rubric: z.string().trim().min(1).max(4_000), +}) + +export type TestEvent = + | { + kind: 'mock' + runId: string + callId: string + key: string + /** The mocked block, or the Agent block that made a mocked tool call. */ + block: string + /** Set when an Agent's tool call is being answered. */ + tool?: string + input: Record + /** A placeholder output shaped like the block's or tool's real one, for `mockSampleOutput`. */ + sample: Record + branchIndex?: number + } + | { kind: 'waiting'; runId: string } + | { + kind: 'done' + runId: string + executionId: string + output: unknown + error?: string + spyCalls: Array<{ key: string; input: unknown; output: unknown }> + /** Mock and spy keys that named a block in this run's workflow. */ + matchedKeys: string[] + } + +const checkSchema = z.object({ + status: z.enum(['pass', 'fail', 'skip']), + line: z.number().int().positive().optional(), + column: z.number().int().positive().optional(), + message: z.string().optional(), + actual: z.unknown().optional(), + expected: z.unknown().optional(), + judge: z.string().optional(), +}) + +export const testReportSchema = z.object({ + topLevel: z.array(z.object({ kind: z.enum(['suite', 'test']), name: z.string() })), + tests: z.array( + z.object({ + path: z.array(z.string()).min(1), + status: z.enum(['pass', 'fail', 'skip']), + durationMs: z.number().nonnegative(), + checks: z.array(checkSchema), + executions: z.array(z.object({ workflow: z.string(), executionId: z.string() })), + logs: z.array(z.string()), + error: z + .object({ + message: z.string(), + line: z.number().int().positive().optional(), + column: z.number().int().positive().optional(), + actual: z.unknown().optional(), + expected: z.unknown().optional(), + }) + .optional(), + }) + ), +}) +export type TestReport = z.infer + +export const testCollectionSchema = z.object({ + topLevel: z.array(z.object({ kind: z.enum(['suite', 'test']), name: z.string() })), + tests: z.array( + z.object({ + path: z.array(z.string()).min(1), + mode: z.enum(['run', 'only', 'skip']), + line: z.number().int().positive(), + endLine: z.number().int().positive(), + }) + ), +}) +export type TestCollection = z.infer diff --git a/apps/sim/lib/workflow-tests/repository.ts b/apps/sim/lib/workflow-tests/repository.ts new file mode 100644 index 00000000000..5ea1d280954 --- /dev/null +++ b/apps/sim/lib/workflow-tests/repository.ts @@ -0,0 +1,452 @@ +import { db } from '@sim/db' +import { + workflow, + workflowDeploymentVersion, + workflowExecutionLogs, + workflowTest, + workflowTestRun, + workspaceFiles, +} from '@sim/db/schema' +import { and, desc, eq, inArray, isNull, sql } from 'drizzle-orm' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' +import type { TestCaseStatus, TestCollection, TestReport } from '@/lib/workflow-tests/protocol' +import type { WorkflowTestVersion } from '@/lib/workflows/application/run-workflow-for-test' + +export type WorkflowTestRow = typeof workflowTest.$inferSelect +export type WorkflowTestRunRow = typeof workflowTestRun.$inferSelect +export type WorkflowTestCases = TestCollection['tests'] + +/** Runs kept per test and version for the list's history dots. */ +export const RECENT_RUNS_PER_TEST = 5 + +export async function insertWorkflowTestInTx( + tx: DbTransaction, + values: typeof workflowTest.$inferInsert +): Promise { + const [row] = await tx.insert(workflowTest).values(values).returning() + return row +} + +export async function getLiveWorkflowTestByName( + workspaceId: string, + name: string, + executor: DbOrTx = db +): Promise { + const [row] = await executor + .select() + .from(workflowTest) + .where( + and( + eq(workflowTest.workspaceId, workspaceId), + eq(workflowTest.name, name), + isNull(workflowTest.deletedAt) + ) + ) + .limit(1) + return row ?? null +} + +export async function getLiveWorkflowTestByBodyFileId( + bodyFileId: string, + executor: DbOrTx = db +): Promise { + const [row] = await executor + .select() + .from(workflowTest) + .where(and(eq(workflowTest.bodyFileId, bodyFileId), isNull(workflowTest.deletedAt))) + .limit(1) + return row ?? null +} + +export async function updateWorkflowTestCases( + testId: string, + cases: WorkflowTestCases, + sourceHash: string, + executor: DbOrTx = db +): Promise { + await executor + .update(workflowTest) + .set({ cases, sourceHash, updatedAt: new Date() }) + .where(eq(workflowTest.id, testId)) +} + +export async function updateWorkflowTestMetadata( + testId: string, + metadata: { title?: string; description?: string | null } +): Promise { + const [row] = await db + .update(workflowTest) + .set({ ...metadata, updatedAt: new Date() }) + .where(eq(workflowTest.id, testId)) + .returning() + return row +} + +export async function listLiveWorkflowTests(workspaceId: string): Promise { + return db + .select() + .from(workflowTest) + .where(and(eq(workflowTest.workspaceId, workspaceId), isNull(workflowTest.deletedAt))) + .orderBy(workflowTest.title) +} + +/** Deletes a test with its source file; soft-delete cleanup later releases the file's storage. */ +export async function softDeleteWorkflowTest(test: { + id: string + bodyFileId: string +}): Promise { + const now = new Date() + await db.transaction(async (tx) => { + await tx + .update(workflowTest) + .set({ deletedAt: now, updatedAt: now }) + .where(eq(workflowTest.id, test.id)) + await tx + .update(workspaceFiles) + .set({ deletedAt: now, updatedAt: now }) + .where( + and( + eq(workspaceFiles.id, test.bodyFileId), + eq(workspaceFiles.context, 'test'), + isNull(workspaceFiles.deletedAt) + ) + ) + }) +} + +/** Every file's run row in one statement, so a failure leaves none of them running. */ +export async function insertWorkflowTestRuns( + rows: Array<{ + id: string + testId: string + workspaceId: string + version: WorkflowTestVersion + triggeredByActor: unknown + triggeredByUserId: string | null + }> +): Promise { + await db.insert(workflowTestRun).values(rows.map((row) => ({ ...row, status: 'running' }))) +} + +/** Files run one after another, so a run's clock starts when its file does, not when it queued. */ +export async function markWorkflowTestRunStarted(runId: string): Promise { + await db + .update(workflowTestRun) + .set({ startedAt: new Date() }) + .where(eq(workflowTestRun.id, runId)) +} + +/** A workflow a run executed and the deployment it ran; `null` is the draft. */ +export interface RanAgainstEntry { + workflowId: string + deploymentVersionId: string | null + /** A `runWorkflow()` execution of it, whose snapshot is the workflow as it ran; null for a child. */ + executionId: string | null + /** For a draft, the workflow's `updatedAt` when the run ended; a later edit makes the run stale. */ + draftUpdatedAt: string | null +} + +type ExecutedWorkflow = Omit + +/** + * Every workflow a run executed: the ones `runWorkflow()` started, from their execution logs, and + * the children they called, which run inside their parent's execution and so log nothing of their + * own. A child ran the same version as its run: its live deployment, or its draft. + */ +export async function readRanAgainst(params: { + executionIds: string[] + enteredWorkflowIds: string[] + workspaceId: string + version: WorkflowTestVersion +}): Promise { + const started = await readExecutedDeployments(params.executionIds, params.workspaceId) + const startedIds = new Set(started.map((entry) => entry.workflowId)) + const childIds = params.enteredWorkflowIds.filter((id) => !startedIds.has(id)) + const children = await readChildVersions(childIds, params.version) + const executed = [...started, ...children] + const drafts = executed.filter((entry) => entry.deploymentVersionId === null) + const updatedAt = new Map( + drafts.length === 0 + ? [] + : ( + await db + .select({ id: workflow.id, updatedAt: workflow.updatedAt }) + .from(workflow) + .where( + inArray( + workflow.id, + drafts.map((entry) => entry.workflowId) + ) + ) + ).map((row) => [row.id, row.updatedAt.toISOString()]) + ) + return executed.map((entry) => ({ + ...entry, + draftUpdatedAt: + entry.deploymentVersionId === null ? (updatedAt.get(entry.workflowId) ?? null) : null, + })) +} + +async function readChildVersions( + workflowIds: string[], + version: WorkflowTestVersion +): Promise { + if (workflowIds.length === 0) return [] + if (version === 'draft') { + return workflowIds.map((workflowId) => ({ + workflowId, + deploymentVersionId: null, + executionId: null, + })) + } + const active = await db + .select({ workflowId: workflowDeploymentVersion.workflowId, id: workflowDeploymentVersion.id }) + .from(workflowDeploymentVersion) + .where( + and( + inArray(workflowDeploymentVersion.workflowId, workflowIds), + eq(workflowDeploymentVersion.isActive, true) + ) + ) + return active.map((row) => ({ + workflowId: row.workflowId, + deploymentVersionId: row.id, + executionId: null, + })) +} + +/** The workflows and deployments these executions ran, from their execution logs. */ +async function readExecutedDeployments( + executionIds: string[], + workspaceId: string +): Promise { + if (executionIds.length === 0) return [] + const rows = await db + .selectDistinctOn( + [workflowExecutionLogs.workflowId, workflowExecutionLogs.deploymentVersionId], + { + workflowId: workflowExecutionLogs.workflowId, + deploymentVersionId: workflowExecutionLogs.deploymentVersionId, + executionId: workflowExecutionLogs.executionId, + } + ) + .from(workflowExecutionLogs) + .where( + and( + inArray(workflowExecutionLogs.executionId, executionIds), + eq(workflowExecutionLogs.workspaceId, workspaceId) + ) + ) + .orderBy(workflowExecutionLogs.workflowId, workflowExecutionLogs.deploymentVersionId) + return rows.flatMap((row) => + row.workflowId + ? [ + { + workflowId: row.workflowId, + deploymentVersionId: row.deploymentVersionId, + executionId: row.executionId, + }, + ] + : [] + ) +} + +export interface DeploymentFacts { + names: Map + /** When each workflow last changed, draft edits included. */ + updatedAt: Map + /** Active deployment per workflow. */ + active: Map + /** Version number of every deployment asked about. */ + versions: Map +} + +/** Names, live deployments, and version numbers needed to tell whether a run is out of date. */ +export async function readDeploymentFacts( + workflowIds: string[], + deploymentVersionIds: string[] +): Promise { + const facts: DeploymentFacts = { + names: new Map(), + updatedAt: new Map(), + active: new Map(), + versions: new Map(), + } + if (workflowIds.length === 0) return facts + const [workflows, active, versions] = await Promise.all([ + db + .select({ id: workflow.id, name: workflow.name, updatedAt: workflow.updatedAt }) + .from(workflow) + .where(and(inArray(workflow.id, workflowIds), isNull(workflow.archivedAt))), + db + .select({ + workflowId: workflowDeploymentVersion.workflowId, + id: workflowDeploymentVersion.id, + version: workflowDeploymentVersion.version, + }) + .from(workflowDeploymentVersion) + .where( + and( + inArray(workflowDeploymentVersion.workflowId, workflowIds), + eq(workflowDeploymentVersion.isActive, true) + ) + ), + deploymentVersionIds.length === 0 + ? Promise.resolve([]) + : db + .select({ id: workflowDeploymentVersion.id, version: workflowDeploymentVersion.version }) + .from(workflowDeploymentVersion) + .where(inArray(workflowDeploymentVersion.id, deploymentVersionIds)), + ]) + for (const row of workflows) { + facts.names.set(row.id, row.name) + facts.updatedAt.set(row.id, row.updatedAt) + } + for (const row of active) facts.active.set(row.workflowId, { id: row.id, version: row.version }) + for (const row of versions) facts.versions.set(row.id, row.version) + return facts +} + +export async function completeWorkflowTestRun( + runId: string, + report: TestReport, + sourceHash: string, + ranAgainst: RanAgainstEntry[] +): Promise { + const count = (status: 'pass' | 'fail' | 'skip') => + report.tests.filter((test) => test.status === status).length + const failed = count('fail') + await db + .update(workflowTestRun) + .set({ + status: failed > 0 ? 'failed' : 'passed', + passed: count('pass'), + failed, + skipped: count('skip'), + report, + sourceHash, + ranAgainst, + completedAt: new Date(), + }) + .where(eq(workflowTestRun.id, runId)) +} + +/** Records one case starting or finishing while its run is still going. */ +export async function recordWorkflowTestProgress( + runId: string, + caseKey: string, + status: TestCaseStatus +): Promise { + await db + .update(workflowTestRun) + .set({ + progress: sql`coalesce(${workflowTestRun.progress}, '{}'::jsonb) || jsonb_build_object(${caseKey}::text, ${status}::text)`, + }) + .where(and(eq(workflowTestRun.id, runId), eq(workflowTestRun.status, 'running'))) +} + +export async function failWorkflowTestRun( + runId: string, + error: string, + sourceHash: string | null +): Promise { + await db + .update(workflowTestRun) + .set({ status: 'error', error, sourceHash, completedAt: new Date() }) + .where(eq(workflowTestRun.id, runId)) +} + +/** The newest runs of each test, at one version or (null) both, newest first, without reports. */ +export async function listRecentWorkflowTestRuns( + testIds: string[], + version: WorkflowTestVersion | null, + perTest: number +): Promise>> { + if (testIds.length === 0) return [] + const ranked = db + .select({ + id: workflowTestRun.id, + rank: sql`row_number() over (partition by ${workflowTestRun.testId} order by ${workflowTestRun.startedAt} desc)`.as( + 'rank' + ), + }) + .from(workflowTestRun) + .where( + and( + inArray(workflowTestRun.testId, testIds), + version ? eq(workflowTestRun.version, version) : undefined + ) + ) + .as('ranked') + return db + .select({ + id: workflowTestRun.id, + testId: workflowTestRun.testId, + workspaceId: workflowTestRun.workspaceId, + version: workflowTestRun.version, + status: workflowTestRun.status, + passed: workflowTestRun.passed, + failed: workflowTestRun.failed, + skipped: workflowTestRun.skipped, + error: workflowTestRun.error, + triggeredByActor: workflowTestRun.triggeredByActor, + triggeredByUserId: workflowTestRun.triggeredByUserId, + sourceHash: workflowTestRun.sourceHash, + ranAgainst: workflowTestRun.ranAgainst, + progress: workflowTestRun.progress, + startedAt: workflowTestRun.startedAt, + completedAt: workflowTestRun.completedAt, + }) + .from(workflowTestRun) + .innerJoin(ranked, eq(ranked.id, workflowTestRun.id)) + .where(sql`${ranked.rank} <= ${perTest}`) + .orderBy(desc(workflowTestRun.startedAt)) +} + +/** The newest run of one test, at one version or (null) either, with its report. */ +export async function getLatestWorkflowTestRun( + testId: string, + version: WorkflowTestVersion | null +): Promise { + const [row] = await db + .select() + .from(workflowTestRun) + .where( + and( + eq(workflowTestRun.testId, testId), + version ? eq(workflowTestRun.version, version) : undefined + ) + ) + .orderBy(desc(workflowTestRun.startedAt)) + .limit(1) + return row ?? null +} + +export async function getWorkflowTestRunById( + testId: string, + runId: string +): Promise { + const [row] = await db + .select() + .from(workflowTestRun) + .where(and(eq(workflowTestRun.id, runId), eq(workflowTestRun.testId, testId))) + .limit(1) + return row ?? null +} + +export async function listWorkflowTestRunsById( + runIds: string[], + workspaceId: string +): Promise> { + if (runIds.length === 0) return [] + const rows = await db + .select({ + run: workflowTestRun, + testName: workflowTest.name, + testSourceHash: workflowTest.sourceHash, + }) + .from(workflowTestRun) + .innerJoin(workflowTest, eq(workflowTest.id, workflowTestRun.testId)) + .where(and(inArray(workflowTestRun.id, runIds), eq(workflowTestRun.workspaceId, workspaceId))) + return rows.map(({ run, testName, testSourceHash }) => ({ ...run, testName, testSourceHash })) +} diff --git a/apps/sim/lib/workflow-tests/run-test-file.ts b/apps/sim/lib/workflow-tests/run-test-file.ts new file mode 100644 index 00000000000..15ac944c457 --- /dev/null +++ b/apps/sim/lib/workflow-tests/run-test-file.ts @@ -0,0 +1,58 @@ +import type { Principal } from '@sim/auth/principal' +import { generateShortId } from '@sim/utils/id' +import { runSandboxTask } from '@/lib/execution/sandbox/run-task' +import { + type TestCaseStatus, + type TestReport, + testReportSchema, +} from '@/lib/workflow-tests/protocol' +import { WorkflowTestSession } from '@/lib/workflow-tests/session' +import { + closeWorkflowTestSession, + openWorkflowTestSession, +} from '@/lib/workflow-tests/session-registry' +import type { WorkflowTestVersion } from '@/lib/workflows/application/run-workflow-for-test' + +export interface RunWorkflowTestFileInput { + principal: Principal + workspaceId: string + /** The test file's source and its path, which stack traces and check lines refer to. */ + source: string + filePath: string + version: WorkflowTestVersion + /** Full test names (`describe > it`) to run; null runs the whole file. */ + only: string[] | null + onProgress: (path: string[], status: TestCaseStatus) => Promise + signal?: AbortSignal +} + +/** + * Runs one test file in the sandbox. Each `runWorkflow()` it makes becomes a real workflow + * run whose mocked blocks are answered by the file's own mocks. + */ +export async function runWorkflowTestFile( + input: RunWorkflowTestFileInput +): Promise<{ report: TestReport; enteredWorkflowIds: string[] }> { + const requestId = generateShortId(12) + const session = new WorkflowTestSession({ + workspaceId: input.workspaceId, + principal: input.principal, + version: input.version, + only: input.only, + onProgress: input.onProgress, + }) + openWorkflowTestSession(requestId, session) + try { + const bytes = await runSandboxTask( + 'workflow-test-run', + { workspaceId: input.workspaceId, code: input.source, codeFilename: input.filePath }, + { requestId, ownerKey: `workspace:${input.workspaceId}`, signal: input.signal } + ) + return { + report: testReportSchema.parse(JSON.parse(Buffer.from(bytes).toString('utf-8'))), + enteredWorkflowIds: [...session.enteredWorkflowIds], + } + } finally { + closeWorkflowTestSession(requestId) + } +} diff --git a/apps/sim/lib/workflow-tests/session-registry.ts b/apps/sim/lib/workflow-tests/session-registry.ts new file mode 100644 index 00000000000..5dfb4402b28 --- /dev/null +++ b/apps/sim/lib/workflow-tests/session-registry.ts @@ -0,0 +1,19 @@ +import type { WorkflowTestSession } from '@/lib/workflow-tests/session' + +/** Live sessions by sandbox request id; each entry is removed when its sandbox run ends. */ +const sessions = new Map() + +export function openWorkflowTestSession(requestId: string, session: WorkflowTestSession): void { + sessions.set(requestId, session) +} + +export function closeWorkflowTestSession(requestId: string): void { + sessions.get(requestId)?.close() + sessions.delete(requestId) +} + +export function requireWorkflowTestSession(requestId: string): WorkflowTestSession { + const session = sessions.get(requestId) + if (!session) throw new Error('This workflow test run is no longer active') + return session +} diff --git a/apps/sim/lib/workflow-tests/session.ts b/apps/sim/lib/workflow-tests/session.ts new file mode 100644 index 00000000000..762e180a677 --- /dev/null +++ b/apps/sim/lib/workflow-tests/session.ts @@ -0,0 +1,610 @@ +import { type Principal, requirePrincipalSubjectUserId } from '@sim/auth/principal' +import { toStringOrNull } from '@sim/utils/coerce' +import { getErrorMessage } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { isRecordLike, toRecord } from '@sim/utils/object' +import type { BlockState } from '@sim/workflow-types/workflow' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { listWorkspaceMcpServers } from '@/lib/mcp/queries' +import { createMcpToolId } from '@/lib/mcp/utils' +import { type JudgeVerdict, judgeRubric } from '@/lib/workflow-tests/judge' +import { + createMockChannel, + type MockChannel, + type MockMatcher, + type ParkedMockCall, +} from '@/lib/workflow-tests/mock-channel' +import type { TestCaseStatus, TestEvent, TestTarget } from '@/lib/workflow-tests/protocol' +import { + runWorkflowForTest, + type WorkflowTestRunResult, + type WorkflowTestVersion, +} from '@/lib/workflows/application/run-workflow-for-test' +import { getEffectiveBlockOutputs } from '@/lib/workflows/blocks/block-outputs' +import { + loadDeployedWorkflowState, + loadWorkflowFromNormalizedTables, +} from '@/lib/workflows/persistence/utils' +import { getActiveWorkflowName, listActiveWorkflowsNamed } from '@/lib/workflows/queries' +import { generateMockPayloadFromOutputsDefinition } from '@/lib/workflows/triggers/mock-payload' +import { resolveTriggerRunOptions } from '@/lib/workflows/triggers/run-options' +import { AGENT, normalizeName } from '@/executor/constants' +import type { TestWorkflowBlock } from '@/executor/execution/types' +import type { ExecutionResult } from '@/executor/types' +import { hasExecutionResult, readAttemptedExecutionId } from '@/executor/utils/errors' +import { projectResolvedSecretModelContent } from '@/executor/utils/resolved-secret-content-projection' +import type { ResolvedSecretTraceRegistry } from '@/executor/utils/resolved-secret-trace-registry' +import { getToolOutputsMetadata } from '@/tools/metadata-outputs' +import { hasToolId } from '@/tools/tool-ids' + +/** How long one `testNext` broker call waits before answering `waiting`, well under the broker timeout. */ +const EVENT_WAIT_MS = 25_000 + +export interface WorkflowTestSessionConfig { + workspaceId: string + principal: Principal + version: WorkflowTestVersion + /** Full test names (`describe > it`) to run; null runs every test. */ + only: string[] | null + /** Records a case starting or finishing while the file runs. */ + onProgress: (path: string[], status: TestCaseStatus) => Promise +} + +interface RunOutcome { + executionId: string + output: unknown + error?: string + logs: NonNullable +} + +function cancellableDelay(ms: number): { promise: Promise<'timeout'>; cancel: () => void } { + let timer: ReturnType | undefined + const promise = new Promise<'timeout'>((resolve) => { + timer = setTimeout(() => resolve('timeout'), ms) + }) + return { promise, cancel: () => clearTimeout(timer) } +} + +async function resolveWorkflowId(workspaceId: string, name: string): Promise { + const matches = await listActiveWorkflowsNamed(workspaceId, name) + if (matches.length === 0) throw new OrchestrationError('not_found', `No workflow named "${name}"`) + if (matches.length > 1) { + throw new OrchestrationError( + 'validation', + `More than one workflow is named "${name}"; rename one` + ) + } + return matches[0].id +} + +async function loadWorkflowBlocks( + workflowId: string, + workspaceId: string, + version: WorkflowTestVersion +): Promise> { + const state = + version === 'draft' + ? await loadWorkflowFromNormalizedTables(workflowId) + : await loadDeployedWorkflowState(workflowId, workspaceId) + if (!state?.blocks) { + throw new OrchestrationError('validation', `The workflow has no ${version} version to test`) + } + return state.blocks +} + +/** + * A child's block names, read only to tell a mock of an unreached block from a misspelled one. + * The draft names them whether or not the child is deployed or ever runs; a deleted child names + * none, and running it fails in the executor if the run reaches it. + */ +async function loadChildBlockNames(workflowId: string): Promise> { + return (await loadWorkflowFromNormalizedTables(workflowId))?.blocks ?? {} +} + +/** + * The trigger block a run starts from: the one the test names, or the workflow's only one. + * A workflow with several triggers must be told which, by name. + */ +function chooseTrigger( + workflow: string, + blocks: Record, + trigger: string | null +): string | undefined { + const options = resolveTriggerRunOptions(blocks) + if (trigger !== null) { + const chosen = options.find( + (option) => normalizeName(option.blockName) === normalizeName(trigger) + ) + if (!chosen) { + throw new OrchestrationError( + 'validation', + `${workflow} has no trigger named "${trigger}". Its triggers: ${options.map((option) => option.blockName).join(', ')}` + ) + } + return chosen.triggerBlockId + } + if (options.length > 1) { + throw new OrchestrationError( + 'validation', + `${workflow} has ${options.length} triggers; choose one with runWorkflow(name, input, { trigger: '…' }): ${options.map((option) => option.blockName).join(', ')}` + ) + } + return undefined +} + +const TOOL_OBJECT_FORMS = + 'an MCP tool as { mcp: "Server name", tool: "tool_name" } or a custom tool as { customTool: "Title" }' + +/** Tools test files may name by id: built-in catalog tools only. */ +function assertKnownTool(toolId: string): void { + if (toolId.startsWith('mcp-') || toolId.startsWith(AGENT.CUSTOM_TOOL_PREFIX)) { + throw new OrchestrationError('validation', `mockTool("${toolId}"): name ${TOOL_OBJECT_FORMS}`) + } + if (!hasToolId(toolId)) { + throw new OrchestrationError( + 'validation', + `mockTool("${toolId}"): there is no tool with that id` + ) + } +} + +/** Rewrites each tool target to the id the executor calls, finding MCP servers by name. */ +async function resolveToolTargets( + workspaceId: string, + targets: TestTarget[] +): Promise { + const needsServers = targets.some((target) => target.kind === 'tool' && target.mcpServer !== null) + const servers = needsServers + ? (await listWorkspaceMcpServers({ workspaceId, sortBy: 'name', sortOrder: 'asc' })).data + : [] + return targets.map((target) => { + if (target.kind !== 'tool') return target + if (target.customTool) { + return { ...target, tool: `${AGENT.CUSTOM_TOOL_PREFIX}${target.tool}`, customTool: false } + } + if (target.mcpServer === null) { + assertKnownTool(target.tool) + return target + } + const wanted = normalizeName(target.mcpServer) + const matches = servers.filter((server) => normalizeName(server.name) === wanted) + if (matches.length !== 1) { + throw new OrchestrationError( + 'validation', + matches.length === 0 + ? `mockTool({ mcp: "${target.mcpServer}" }): this workspace has no MCP server with that name. Its servers: ${servers.map((server) => server.name).join(', ') || 'none'}` + : `mockTool({ mcp: "${target.mcpServer}" }): ${matches.length} MCP servers share that name; rename one` + ) + } + return { ...target, tool: createMcpToolId(matches[0].id, target.tool), mcpServer: null } + }) +} + +/** Custom tool ids carry the title, which tests may write in any case or spacing. */ +function sameTool(mockToolId: string, toolId: string): boolean { + if (mockToolId === toolId) return true + const prefix = AGENT.CUSTOM_TOOL_PREFIX + return ( + mockToolId.startsWith(prefix) && + toolId.startsWith(prefix) && + normalizeName(mockToolId.slice(prefix.length)) === normalizeName(toolId.slice(prefix.length)) + ) +} + +/** Placeholder output shaped like a block's real one, for its configured operation. */ +function sampleBlockOutput(block: TestWorkflowBlock | undefined): Record { + if (!block) return {} + const subBlocks: Record = {} + for (const [key, value] of Object.entries(block.params)) subBlocks[key] = { value } + return generateMockPayloadFromOutputsDefinition(getEffectiveBlockOutputs(block.type, subBlocks)) +} + +function sampleToolOutput(toolId: string): Record { + const outputs = getToolOutputsMetadata(toolId) + return outputs ? generateMockPayloadFromOutputsDefinition(outputs) : {} +} + +interface ToolMock { + /** The Agent block it answers for; null answers every Agent. */ + blockId: string | null + toolId: string + key: string +} + +/** The child a workflow block calls, when it names one by a fixed id rather than a reference. */ +function childWorkflowId(id: unknown): string | undefined { + return typeof id === 'string' && id.trim() !== '' && !id.includes('<') ? id : undefined +} + +/** The mock answering this Agent's call to a tool: one named for that Agent wins over a general one. */ +function toolMockFor(mocks: ToolMock[], blockId: string, toolId: string): ToolMock | undefined { + return ( + mocks.find((mock) => sameTool(mock.toolId, toolId) && mock.blockId === blockId) ?? + mocks.find((mock) => sameTool(mock.toolId, toolId) && mock.blockId === null) + ) +} + +/** + * Matches one `runWorkflow()` call's mocks and spies to blocks by name, as each workflow it + * executes (the tested one and every child) announces its blocks. A target naming a workflow + * only reaches that workflow's blocks; an unnamed one reaches every workflow in the run. + */ +class RunMatcher implements MockMatcher { + private readonly blocks = new Map() + private readonly mockKeyByBlockId = new Map() + private readonly spyKeyByBlockId = new Map() + private readonly toolMocks: ToolMock[] = [] + private readonly workflowNames = new Map() + /** Keys whose target named a block in some workflow this run executed. */ + readonly matchedKeys = new Set() + readonly spyCalls: Array<{ key: string; input: unknown; output: unknown }> = [] + private readonly targets: TestTarget[] + + /** Workflows whose block names are known: every one that ran, and the children they call. */ + private readonly knownWorkflows = new Set() + /** Whether the tested workflow started; secrets resolve only after it has. */ + entered = false + /** The tested workflow's registry, which redacts its output before the test sees it. */ + outputRegistry: ResolvedSecretTraceRegistry | undefined + + constructor( + targets: TestTarget[], + private readonly loadChildBlocks: (workflowId: string) => Promise>, + private readonly onEnter: (workflowId: string) => void + ) { + this.targets = [...targets].sort( + (a, b) => Number(a.workflow === null) - Number(b.workflow === null) + ) + } + + async enterWorkflow({ + workflowId, + blocks, + resolvedSecretTraceRegistry, + }: { + workflowId: string + blocks: TestWorkflowBlock[] + resolvedSecretTraceRegistry: ResolvedSecretTraceRegistry | undefined + }): Promise { + if (!this.entered) { + this.entered = true + this.outputRegistry = resolvedSecretTraceRegistry + } + this.onEnter(workflowId) + const workflowName = await this.workflowName(workflowId) + for (const block of blocks) this.blocks.set(block.id, block) + await this.learnWorkflow( + workflowId, + blocks.map((block) => ({ + name: block.name, + childWorkflowId: childWorkflowId(block.params.workflowId), + })) + ) + for (const target of this.targets) { + if (target.workflow !== null && normalizeName(target.workflow) !== workflowName) continue + if (target.kind === 'tool' && target.block === null) { + if (!this.toolMocks.some((mock) => mock.key === target.key)) { + this.toolMocks.push({ blockId: null, toolId: target.tool, key: target.key }) + } + this.matchedKeys.add(target.key) + continue + } + const targetName = normalizeName(target.block ?? '') + for (const block of blocks) { + if (normalizeName(block.name) !== targetName) continue + this.matchedKeys.add(target.key) + if (target.kind === 'tool') { + if ( + !this.toolMocks.some((mock) => mock.key === target.key && mock.blockId === block.id) + ) { + this.toolMocks.push({ blockId: block.id, toolId: target.tool, key: target.key }) + } + } else if (target.kind === 'mock') { + if (!this.mockKeyByBlockId.has(block.id)) this.mockKeyByBlockId.set(block.id, target.key) + this.spyKeyByBlockId.delete(block.id) + } else if (!this.mockKeyByBlockId.has(block.id) && !this.spyKeyByBlockId.has(block.id)) { + this.spyKeyByBlockId.set(block.id, target.key) + } + } + } + } + + /** + * Marks the targets naming a block of this workflow as real, then does the same for each + * child it calls by a fixed id. A mock of a child the run never reached is set up, not a typo. + */ + private async learnWorkflow( + workflowId: string, + blocks: Array<{ name: string; childWorkflowId: string | undefined }> + ): Promise { + if (this.knownWorkflows.has(workflowId)) return + this.knownWorkflows.add(workflowId) + const workflowName = await this.workflowName(workflowId) + const names = new Set(blocks.map((block) => normalizeName(block.name))) + for (const target of this.targets) { + if (target.workflow !== null && normalizeName(target.workflow) !== workflowName) continue + if (target.block === null || names.has(normalizeName(target.block))) { + this.matchedKeys.add(target.key) + } + } + for (const block of blocks) { + if (!block.childWorkflowId || this.knownWorkflows.has(block.childWorkflowId)) continue + const childBlocks = await this.loadChildBlocks(block.childWorkflowId) + await this.learnWorkflow( + block.childWorkflowId, + Object.values(childBlocks).map((child) => ({ + name: child.name, + childWorkflowId: childWorkflowId(child.subBlocks?.workflowId?.value), + })) + ) + } + } + + private async workflowName(workflowId: string): Promise { + const known = this.workflowNames.get(workflowId) + if (known !== undefined) return known + const name = normalizeName((await getActiveWorkflowName(workflowId)) ?? workflowId) + this.workflowNames.set(workflowId, name) + return name + } + + mocksBlock(blockId: string): boolean { + return this.mockKeyByBlockId.has(blockId) + } + + mocksTool(blockId: string, toolId: string): boolean { + return toolMockFor(this.toolMocks, blockId, toolId) !== undefined + } + + spiesBlock(blockId: string): boolean { + return this.spyKeyByBlockId.has(blockId) + } + + recordSpy(call: { blockId: string; input: unknown; output: unknown }): void { + const key = this.spyKeyByBlockId.get(call.blockId) + if (key) this.spyCalls.push({ key, input: call.input, output: call.output }) + } + + mockKey(blockId: string): string | undefined { + return this.mockKeyByBlockId.get(blockId) + } + + toolMockKey(blockId: string, toolId: string): string | undefined { + return toolMockFor(this.toolMocks, blockId, toolId)?.key + } + + block(blockId: string): TestWorkflowBlock | undefined { + return this.blocks.get(blockId) + } +} + +/** One `runWorkflow()` call: an executor run whose mocked blocks park on `channel`. */ +class TestWorkflowRun { + readonly id = generateId() + private pendingCall: Promise | undefined + private readonly outcome: Promise + + constructor( + private readonly channel: MockChannel, + execution: Promise, + private readonly abort: AbortController, + private readonly matcher: RunMatcher + ) { + this.outcome = execution.then( + ({ executionId, result }) => this.toOutcome(executionId, result), + (error: unknown) => { + const executionId = readAttemptedExecutionId(error) ?? '' + if (hasExecutionResult(error)) return this.toOutcome(executionId, error.executionResult) + return { + executionId, + ...this.redact({ output: null, error: getErrorMessage(error) }), + logs: [], + } + } + ) + void this.outcome.finally(() => this.channel.close(new Error('The workflow run finished'))) + } + + private toOutcome(executionId: string, result: ExecutionResult): RunOutcome { + const error = + result.status === 'paused' + ? 'The workflow paused for human input. Mock the human-in-the-loop block to test past it.' + : result.success + ? undefined + : (result.error ?? 'The workflow run failed') + const redacted = this.redact({ output: result.output, error }) + return { executionId, ...redacted, logs: result.logs ?? [] } + } + + /** What crosses into the sandbox, with this run's resolved secrets replaced; fails closed. */ + private redact(value: { output: unknown; error: string | undefined }): { + output: unknown + error?: string + } { + if (!this.matcher.entered) + return { output: null, ...(value.error ? { error: value.error } : {}) } + const projection = projectResolvedSecretModelContent(value, this.matcher.outputRegistry) + if (!projection.safe) { + return { output: null, error: 'The workflow result could not be redacted of its secrets' } + } + const projected = toRecord(projection.value) + const error = toStringOrNull(projected.error) + return { output: projected.output ?? null, ...(error ? { error } : {}) } + } + + async next(): Promise { + this.pendingCall ??= this.channel.nextCall() + this.pendingCall.catch(() => {}) + const delay = cancellableDelay(EVENT_WAIT_MS) + try { + const winner = await Promise.race([ + this.pendingCall.then((call) => ({ type: 'call' as const, call })), + this.outcome.then((outcome) => ({ type: 'done' as const, outcome })), + delay.promise.then(() => ({ type: 'waiting' as const })), + ]) + if (winner.type === 'waiting') return { kind: 'waiting', runId: this.id } + if (winner.type === 'done') return this.doneEvent(winner.outcome) + this.pendingCall = undefined + return this.mockEvent(winner.call) + } finally { + delay.cancel() + } + } + + private mockEvent(parked: ParkedMockCall): TestEvent { + if (parked.kind === 'tool') { + const { blockId, toolId, input } = parked.call + const key = this.matcher.toolMockKey(blockId, toolId) + if (!key) throw new Error(`Tool ${toolId} was parked without a mock`) + return { + kind: 'mock', + runId: this.id, + callId: parked.callId, + key, + block: this.matcher.block(blockId)?.name ?? blockId, + tool: toolId, + input, + sample: sampleToolOutput(toolId), + } + } + const { blockId, blockName, input, branchIndex } = parked.call + const key = this.matcher.mockKey(blockId) + if (!key) throw new Error(`Block ${blockName} was parked without a mock`) + return { + kind: 'mock', + runId: this.id, + callId: parked.callId, + key, + block: blockName, + input, + sample: sampleBlockOutput(this.matcher.block(blockId)), + ...(branchIndex !== undefined ? { branchIndex } : {}), + } + } + + private doneEvent(outcome: RunOutcome): TestEvent { + return { + kind: 'done', + runId: this.id, + executionId: outcome.executionId, + output: outcome.output, + ...(outcome.error ? { error: outcome.error } : {}), + spyCalls: this.matcher.spyCalls, + matchedKeys: [...this.matcher.matchedKeys], + } + } + + reply(callId: string, reply: { output: unknown } | { error: string }): void { + if ('error' in reply) { + this.channel.reply(callId, { error: reply.error }) + return + } + if (!isRecordLike(reply.output)) { + this.channel.reply(callId, { + error: 'A mocked block must return an object, like { ok: true }', + }) + return + } + this.channel.reply(callId, { output: reply.output }) + } + + cancel(): void { + this.abort.abort(new Error('The test finished')) + this.channel.close(new Error('The test finished')) + } +} + +/** + * Host state for one test file's sandbox run: the workflow runs its tests started, keyed by + * run id. The brokers reach it through the sandbox request id. + */ +export class WorkflowTestSession { + private readonly runs = new Map() + /** Every workflow this file's runs executed, children included. */ + readonly enteredWorkflowIds = new Set() + /** Set when the sandbox run ends; a start still awaiting its lookups then starts nothing. */ + private closed = false + /** Stops `toMatchRubric` model calls still in flight when the sandbox run ends. */ + private readonly judgeAbort = new AbortController() + + constructor(readonly config: WorkflowTestSessionConfig) {} + + async start(args: { + workflow: string + trigger: string | null + input: unknown + targets: TestTarget[] + }): Promise { + const { workspaceId, principal, version } = this.config + const workflowId = await resolveWorkflowId(workspaceId, args.workflow) + const blocks = await loadWorkflowBlocks(workflowId, workspaceId, version) + const triggerBlockId = chooseTrigger(args.workflow, blocks, args.trigger) + const targets = await resolveToolTargets(workspaceId, args.targets) + if (this.closed) throw new Error('This workflow test run has ended') + const matcher = new RunMatcher(targets, loadChildBlockNames, (id) => + this.enteredWorkflowIds.add(id) + ) + const channel = createMockChannel(matcher) + const abort = new AbortController() + const execution = runWorkflowForTest({ + principal, + workflowId, + workspaceId, + version, + workflowInput: args.input, + triggerBlockId, + testHooks: channel.hooks, + abortSignal: abort.signal, + }) + const run = new TestWorkflowRun(channel, execution, abort, matcher) + this.runs.set(run.id, run) + return run.next() + } + + progress(args: { path: string[]; status: TestCaseStatus }): Promise { + return this.config.onProgress(args.path, args.status) + } + + next(runId: string): Promise { + return this.requireRun(runId).next() + } + + reply( + runId: string, + callId: string, + reply: { output: unknown } | { error: string } + ): Promise { + const run = this.requireRun(runId) + run.reply(callId, reply) + return run.next() + } + + cancel(runIds: string[]): void { + for (const runId of runIds) { + this.runs.get(runId)?.cancel() + this.runs.delete(runId) + } + } + + close(): void { + this.closed = true + this.judgeAbort.abort(new Error('The test run ended')) + this.cancel([...this.runs.keys()]) + } + + judge(args: { value: string; rubric: string }): Promise { + return judgeRubric({ + workspaceId: this.config.workspaceId, + actorUserId: requirePrincipalSubjectUserId(this.config.principal), + value: args.value, + rubric: args.rubric, + abortSignal: this.judgeAbort.signal, + }) + } + + private requireRun(runId: string): TestWorkflowRun { + const run = this.runs.get(runId) + if (!run) throw new Error(`Unknown workflow run "${runId}"`) + return run + } +} diff --git a/apps/sim/lib/workflow-tests/source-write.ts b/apps/sim/lib/workflow-tests/source-write.ts new file mode 100644 index 00000000000..212e5aeef2f --- /dev/null +++ b/apps/sim/lib/workflow-tests/source-write.ts @@ -0,0 +1,39 @@ +import { isUtf8 } from 'node:buffer' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { DbOrTx } from '@/lib/db/types' +import { readWorkflowTestCases, testSourceHash } from '@/lib/workflow-tests/definition' +import { + getLiveWorkflowTestByBodyFileId, + updateWorkflowTestCases, +} from '@/lib/workflow-tests/repository' + +/** Test files are source code; anything larger is not a test file. */ +export const MAX_TEST_SOURCE_BYTES = 512 * 1024 + +function decodeTestSource(content: Buffer): string { + if (content.length > MAX_TEST_SOURCE_BYTES) { + throw new OrchestrationError( + 'payload_too_large', + `A test file can be at most ${MAX_TEST_SOURCE_BYTES / 1024}KB` + ) + } + if (!isUtf8(content)) throw new OrchestrationError('validation', 'A test file must be UTF-8 text') + return content.toString('utf-8') +} + +/** Validates new source for an existing test file; the returned step records its cases. */ +export async function prepareWorkflowTestSourceWrite(input: { + fileId: string + workspaceId: string + content: Buffer +}): Promise<(tx: DbOrTx) => Promise> { + const test = await getLiveWorkflowTestByBodyFileId(input.fileId) + if (!test) throw new OrchestrationError('not_found', 'File not found') + const source = decodeTestSource(input.content) + const cases = await readWorkflowTestCases({ + workspaceId: input.workspaceId, + name: test.name, + source, + }) + return (tx) => updateWorkflowTestCases(test.id, cases, testSourceHash(source), tx) +} diff --git a/apps/sim/lib/workflows/application/run-workflow-for-test.ts b/apps/sim/lib/workflows/application/run-workflow-for-test.ts new file mode 100644 index 00000000000..e6d81c96528 --- /dev/null +++ b/apps/sim/lib/workflows/application/run-workflow-for-test.ts @@ -0,0 +1,162 @@ +import { + type Principal, + requirePrincipalSubjectUserId, + type WorkflowExecutionPrincipal, +} from '@sim/auth/principal' +import { generateId } from '@sim/utils/id' +import { getActivelyBannedUserIds } from '@/lib/auth/ban' +import { + releaseExecutionSlot, + reserveExecutionSlot, +} from '@/lib/billing/calculations/usage-reservation' +import { resolveBillingAttribution } from '@/lib/billing/core/billing-attribution' +import { checkExecutionUsageLimits } from '@/lib/billing/core/usage-gate-cache' +import { getExecutionTimeout, RESERVATION_TTL_BUFFER_MS } from '@/lib/core/execution-limits/types' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { generateRequestId } from '@/lib/core/utils/request' +import { resolveActiveWorkflowApplicationContext } from '@/lib/workflows/application/context' +import { resolveTriggerExecution } from '@/lib/workflows/application/run-workflow-from-copilot' +import { executeWorkflow } from '@/lib/workflows/executor/execute-workflow' +import type { ExecutionTestHooks } from '@/executor/execution/types' +import type { ExecutionResult } from '@/executor/types' +import { attachAttemptedExecutionId } from '@/executor/utils/errors' + +export type WorkflowTestVersion = 'draft' | 'deployed' + +export interface WorkflowTestRunResult { + executionId: string + result: ExecutionResult +} + +export interface RunWorkflowForTestInput { + principal: Principal + workflowId: string + /** The test's workspace; a workflow from any other workspace is not found. */ + workspaceId: string + version: WorkflowTestVersion + workflowInput: unknown + /** The trigger to start from; omitted, the workflow's only runnable trigger. */ + triggerBlockId?: string + testHooks: ExecutionTestHooks + abortSignal: AbortSignal +} + +/** + * One workflow run inside a workflow test: the workflow's single runnable trigger, the + * test's input, and its mocks installed as executor test hooks. Billed and logged like any + * run, under the `test` trigger. + * + * A step of the `workflow_tests.run` operation, which authorized the caller's write access to + * the workspace before any test ran; nothing else may call it. Each nested run is not + * authorized again, so a delegated caller's token cannot lapse halfway through a suite. + */ +export async function runWorkflowForTest( + input: RunWorkflowForTestInput +): Promise { + const context = await resolveActiveWorkflowApplicationContext({ + workflowId: input.workflowId, + assertedWorkspaceId: input.workspaceId, + }) + const principal = requireTestPrincipal(input.principal) + const useDraftState = input.version === 'draft' + const prepared = await resolveTriggerExecution({ + input: { + workflowId: input.workflowId, + useDraftState, + workflowInput: input.workflowInput, + hasWorkflowInput: true, + ...(input.triggerBlockId ? { triggerBlockId: input.triggerBlockId } : {}), + useMockPayload: false, + }, + workspaceId: context.workspaceId, + }) + // actorless-unsupported: tests run only for session, personal-key, OAuth, or delegated principals; workflow_tests.run admits no executor or workspace-key caller. + const actorUserId = requirePrincipalSubjectUserId(principal) + // A suite outlives the request that started it, so a ban mid-suite stops the next run. + if ((await getActivelyBannedUserIds([actorUserId])).length > 0) { + throw new OrchestrationError('forbidden', 'This account is suspended') + } + const executionId = generateId() + const billingAttribution = await resolveBillingAttribution({ + actorUserId, + workspaceId: context.workspaceId, + }) + const usage = await checkExecutionUsageLimits(billingAttribution) + if (usage.isExceeded) { + throw new OrchestrationError('forbidden', usage.message ?? 'Usage limit exceeded') + } + if (usage.payerUsage) { + const plan = billingAttribution.payerSubscription?.plan + const reservation = await reserveExecutionSlot({ + billingEntity: billingAttribution.billingEntity, + reservationId: executionId, + plan, + enterpriseConcurrencyLimit: billingAttribution.payerSubscription?.enterpriseConcurrencyLimit, + currentUsage: usage.payerUsage.currentUsage, + limit: usage.payerUsage.limit, + expiresAt: Date.now() + getExecutionTimeout(plan, 'sync') + RESERVATION_TTL_BUFFER_MS, + ...(billingAttribution.organizationId && + usage.memberUsage?.limit !== null && + usage.memberUsage?.limit !== undefined + ? { + member: { + organizationId: billingAttribution.organizationId, + actorUserId: billingAttribution.actorUserId, + currentUsage: usage.memberUsage.currentUsage, + limit: usage.memberUsage.limit, + }, + } + : {}), + }) + if (!reservation.reserved) { + throw new OrchestrationError( + 'forbidden', + `The workflow cannot start now (${reservation.reason}); wait for running executions to finish` + ) + } + } + try { + const result = await executeWorkflow( + { + id: context.workflowId, + userId: context.workflow.userId, + workspaceId: context.workspaceId, + variables: context.workflow.variables || {}, + }, + generateRequestId(), + prepared.input, + actorUserId, + { + enabled: true, + principal, + useDraftState, + workflowTriggerType: 'test', + enforceCredentialAccess: true, + triggerBlockId: prepared.triggerBlockId, + abortSignal: input.abortSignal, + billingAttribution, + testHooks: input.testHooks, + }, + executionId + ) + return { executionId, result } + } catch (error) { + attachAttemptedExecutionId(error, executionId) + throw error + } finally { + await releaseExecutionSlot(executionId) + } +} + +/** The principal kinds `workflow_tests.run` admits; anything else reaching here is a wiring bug. */ +function requireTestPrincipal(principal: Principal): WorkflowExecutionPrincipal { + if ( + principal.kind === 'session' || + principal.kind === 'personal_api_key' || + principal.kind === 'oauth_access_token' + ) { + return principal + } + if (principal.kind === 'delegated' && principal.serviceId === 'copilot') return principal + throw new Error(`A ${principal.kind} principal cannot run workflow tests`) +} diff --git a/apps/sim/lib/workflows/application/run-workflow-from-copilot.ts b/apps/sim/lib/workflows/application/run-workflow-from-copilot.ts index 97300d598ef..56bb9b37fc8 100644 --- a/apps/sim/lib/workflows/application/run-workflow-from-copilot.ts +++ b/apps/sim/lib/workflows/application/run-workflow-from-copilot.ts @@ -94,7 +94,10 @@ interface SnapshotCopilotRunInput extends BaseCopilotRunInput { export interface RunFromBlockFromCopilotInput extends SnapshotCopilotRunInput {} export interface RunBlockFromCopilotInput extends SnapshotCopilotRunInput {} -async function loadDefinition(input: BaseCopilotRunInput, workspaceId: string) { +async function loadDefinition( + input: Pick, + workspaceId: string +) { if (input.useDraftState) return loadWorkflowFromNormalizedTables(input.workflowId) try { return await loadDeployedWorkflowState(input.workflowId, workspaceId) @@ -104,8 +107,20 @@ async function loadDefinition(input: BaseCopilotRunInput, workspaceId: string) { } } -async function resolveTriggerExecution(params: { - input: TriggerCopilotRunInput +export type TriggerExecutionRequest = Pick< + TriggerCopilotRunInput, + | 'workflowId' + | 'useDraftState' + | 'triggerBlockId' + | 'workflowInput' + | 'hasWorkflowInput' + | 'useMockPayload' + | 'inputFromExecutionId' +> + +/** Picks the workflow's runnable trigger and validates the run input against it. */ +export async function resolveTriggerExecution(params: { + input: TriggerExecutionRequest workspaceId: string }): Promise<{ triggerBlockId: string; input: unknown }> { const state = await loadDefinition(params.input, params.workspaceId) diff --git a/apps/sim/lib/workflows/executor/execute-workflow.ts b/apps/sim/lib/workflows/executor/execute-workflow.ts index 01c14e76eb8..10167c86562 100644 --- a/apps/sim/lib/workflows/executor/execute-workflow.ts +++ b/apps/sim/lib/workflows/executor/execute-workflow.ts @@ -16,6 +16,7 @@ import { ExecutionSnapshot } from '@/executor/execution/snapshot' import type { BlockCompletionCallbackData, ExecutionMetadata, + ExecutionTestHooks, SerializableExecutionState, } from '@/executor/execution/types' import type { ExecutionResult, StreamingExecution } from '@/executor/types' @@ -61,6 +62,8 @@ export interface ExecuteWorkflowOptions { workflowStateOverride?: NonNullable /** Stop execution after this block completes. Used for "run until block" feature. */ stopAfterBlockId?: string + /** Workflow test run: mocked blocks await these hooks instead of running. */ + testHooks?: ExecutionTestHooks /** Run-from-block configuration using a prior execution snapshot. */ runFromBlock?: { startBlockId: string @@ -226,6 +229,7 @@ export async function executeWorkflow( base64MaxBytes: streamConfig?.base64MaxBytes, abortSignal: streamConfig?.abortSignal, stopAfterBlockId: streamConfig?.stopAfterBlockId, + testHooks: streamConfig?.testHooks, trustedInitialResolvedSecretTraceProvenance: streamConfig?.trustedInitialResolvedSecretTraceProvenance, runFromBlock: streamConfig?.runFromBlock, diff --git a/apps/sim/lib/workflows/executor/execution-core.ts b/apps/sim/lib/workflows/executor/execution-core.ts index 85a368a5d57..1fafc7cb337 100644 --- a/apps/sim/lib/workflows/executor/execution-core.ts +++ b/apps/sim/lib/workflows/executor/execution-core.ts @@ -55,6 +55,7 @@ import type { ChildWorkflowContext, ContextExtensions, ExecutionCallbacks, + ExecutionTestHooks, IterationContext, SerializableExecutionState, } from '@/executor/execution/types' @@ -140,6 +141,7 @@ export interface ExecuteWorkflowCoreOptions { includeFileBase64?: boolean base64MaxBytes?: number stopAfterBlockId?: string + testHooks?: ExecutionTestHooks /** Trusted encrypted provenance captured by a server-only pre-execution boundary. */ trustedInitialResolvedSecretTraceProvenance?: ResolvedSecretTraceProvenanceV1 /** Immutable deployment admitted by the durable parent log for a resumed execution. */ @@ -631,6 +633,7 @@ async function executeWorkflowCoreImpl( abortSignal, includeFileBase64, base64MaxBytes, + testHooks, runFromBlock, resumeDeploymentVersionId, draftState, @@ -1250,6 +1253,7 @@ async function executeWorkflowCoreImpl( includeFileBase64, base64MaxBytes, stopAfterBlockId: resolvedStopAfterBlockId, + ...(testHooks ? { testHooks } : {}), onChildWorkflowInstanceReady, callChain: metadata.callChain, // The live block stream has a single known, authenticated Sim viewer only on diff --git a/apps/sim/lib/workflows/queries.ts b/apps/sim/lib/workflows/queries.ts index f989b3e4d49..6a2c33f16bd 100644 --- a/apps/sim/lib/workflows/queries.ts +++ b/apps/sim/lib/workflows/queries.ts @@ -119,6 +119,31 @@ export async function listWorkspaceWorkflows(input: ListWorkspaceWorkflowsInput) return keysetPage(keys, rows, input.limit) } +/** Active workflows in a workspace with exactly this name; names are not unique, so callers decide. */ +export async function listActiveWorkflowsNamed(workspaceId: string, name: string) { + return db + .select({ id: workflow.id, name: workflow.name }) + .from(workflow) + .where( + and( + eq(workflow.workspaceId, workspaceId), + isNull(workflow.archivedAt), + eq(workflow.name, name) + ) + ) + .limit(2) +} + +/** An active workflow's name by id, or null when it is archived or gone. */ +export async function getActiveWorkflowName(workflowId: string): Promise { + const [row] = await db + .select({ name: workflow.name }) + .from(workflow) + .where(and(eq(workflow.id, workflowId), isNull(workflow.archivedAt))) + .limit(1) + return row?.name ?? null +} + /** * Loads one consistent workflow record + normalized definition snapshot. Both * the editor route and public metadata route derive their own response from diff --git a/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts index 451900fa920..84c06aa01b5 100644 --- a/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/edit-workspace-file-content.ts @@ -1,5 +1,6 @@ import { isUtf8 } from 'node:buffer' import { AuditAction, AuditResourceType } from '@sim/audit' +import type { Principal } from '@sim/auth/principal' import { resolvePrincipalAttribution } from '@sim/auth/principal' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' @@ -19,6 +20,7 @@ import { defineAuthorizedWorkspaceFileUseCase } from '@/lib/workspace-files/appl import { parseWorkspaceFileRevision } from '@/lib/workspace-files/application/file-revision' import { resolveWorkspaceFileVersionWrite } from '@/lib/workspace-files/application/file-version-write' import { fileOperations } from '@/lib/workspace-files/application/operations' +import { prepareOwnedFileContentWrite } from '@/lib/workspace-files/application/owned-file-content' import { resolveActiveWorkspaceFileContext } from '@/lib/workspace-files/application/workspace-file-context' import { applyWorkspaceFileContentEdit, @@ -73,8 +75,13 @@ export interface EditWorkspaceFileContentResult { */ export const editWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.updateContent, - resolveContext: ({ input }: { input: EditWorkspaceFileContentInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: EditWorkspaceFileContentInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ principal, input, context }): Promise { const lockKey = `file-edit:${context.workspaceId}:${context.fileId}` const lockValue = `${Date.now()}-${generateShortId()}` @@ -92,7 +99,9 @@ export const editWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ } try { - const file = await getWorkspaceFileWithCurrentVersion(context.workspaceId, context.fileId) + const file = await getWorkspaceFileWithCurrentVersion(context.workspaceId, context.fileId, { + includeTestFiles: true, + }) if (!file) throw new OrchestrationError('not_found', 'File not found') if (!file.contentUpdatedAt) { throw new OrchestrationError( @@ -174,6 +183,7 @@ export const editWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ ) } + const commitOwner = await prepareOwnedFileContentWrite(context, content) const attribution = resolvePrincipalAttribution(principal, { workspaceBillingOwnerUserId: context.billedAccountUserId, }) @@ -191,6 +201,7 @@ export const editWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ * The caller's own revision when it sent one, so the guard covers everything since * the content it read — not merely since this use case loaded the file. */ + commitOwner, expectedUpdatedAt: input.expectedRevision ? parseWorkspaceFileRevision(input.expectedRevision, context.fileId) : file.contentUpdatedAt, diff --git a/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.test.ts b/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.test.ts index 844f2108b24..8509d915365 100644 --- a/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.test.ts +++ b/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.test.ts @@ -109,6 +109,7 @@ describe('exportWorkspaceFileSnapshot', () => { expect(mocks.getFile).toHaveBeenCalledWith(WORKSPACE_ID, 'image-1', { throwOnError: true, includeChatUploads: true, + includeTestFiles: true, }) expect(mocks.resolvePermission).toHaveBeenCalledTimes(3) expect(mocks.audit).toHaveBeenCalledOnce() diff --git a/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.ts b/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.ts index 21043a036cf..32f5f4be6e1 100644 --- a/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.ts +++ b/apps/sim/lib/workspace-files/application/export-workspace-file-snapshot.ts @@ -1,4 +1,5 @@ import { AuditAction, AuditResourceType } from '@sim/audit' +import type { Principal } from '@sim/auth/principal' import { PASTE_LIMITS, utf8ByteLength } from '@sim/utils/paste' import { asOrchestrationError, OrchestrationError } from '@/lib/core/orchestration/types' import { MATERIALIZE_CONCURRENCY, mapWithConcurrency } from '@/lib/core/utils/concurrency' @@ -21,10 +22,18 @@ export interface ExportWorkspaceFileSnapshotInput { /** Exports the visible snapshot without writing or advancing the collaborative document. */ export const exportWorkspaceFileSnapshot = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.download, - resolveContext: ({ input }: { input: ExportWorkspaceFileSnapshotInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ExportWorkspaceFileSnapshotInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ principal, input, context }) { - const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true }) + const file = await getWorkspaceFile(context.workspaceId, context.fileId, { + throwOnError: true, + includeTestFiles: true, + }) if (!file) throw new OrchestrationError('not_found', 'File not found') if (!isMarkdownFile(file) && file.type !== 'text/x-markdown') { throw new OrchestrationError('validation', 'Only Markdown files support snapshot export') diff --git a/apps/sim/lib/workspace-files/application/file-versions.ts b/apps/sim/lib/workspace-files/application/file-versions.ts index 1b7cd7da919..4dc23ce42a8 100644 --- a/apps/sim/lib/workspace-files/application/file-versions.ts +++ b/apps/sim/lib/workspace-files/application/file-versions.ts @@ -41,6 +41,7 @@ import { import { parseWorkspaceFileRevision } from '@/lib/workspace-files/application/file-revision' import { resolveWorkspaceFileVersionWrite } from '@/lib/workspace-files/application/file-version-write' import { fileOperations } from '@/lib/workspace-files/application/operations' +import { prepareOwnedFileContentWrite } from '@/lib/workspace-files/application/owned-file-content' import { extractWorkspaceFileRecordText, type ReadWorkspaceFileTextInput, @@ -105,7 +106,10 @@ export interface RevertWorkspaceFileVersionResult { } async function loadActiveFile(context: ActiveWorkspaceFileContext): Promise { - const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true }) + const file = await getWorkspaceFile(context.workspaceId, context.fileId, { + throwOnError: true, + includeTestFiles: true, + }) if (!file) throw new OrchestrationError('not_found', 'File not found') return file } @@ -172,8 +176,13 @@ function recordAtVersion( export const listWorkspaceFileVersions = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.listVersions, - resolveContext: ({ input }: { input: ListWorkspaceFileVersionsInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ListWorkspaceFileVersionsInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context }): Promise { const file = await loadActiveFile(context) const { versions, nextKeys } = await queryWorkspaceFileVersions(file, { @@ -187,8 +196,8 @@ export const listWorkspaceFileVersions = defineAuthorizedWorkspaceFileUseCase({ export const readWorkspaceFileVersion = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readVersion, - resolveContext: ({ input }: { input: FileVersionRef }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ principal, input }: { principal: Principal; input: FileVersionRef }) => + resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context }): Promise { const file = await loadActiveFile(context) return { version: await loadVersion(file, input.version) } @@ -197,8 +206,13 @@ export const readWorkspaceFileVersion = defineAuthorizedWorkspaceFileUseCase({ export const readWorkspaceFileVersionText = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readVersionContent, - resolveContext: ({ input }: { input: ReadWorkspaceFileVersionTextInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ReadWorkspaceFileVersionTextInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context, @@ -224,8 +238,8 @@ export const readWorkspaceFileVersionText = defineAuthorizedWorkspaceFileUseCase export const downloadWorkspaceFileVersion = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.downloadVersion, - resolveContext: ({ input }: { input: FileVersionRef }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ principal, input }: { principal: Principal; input: FileVersionRef }) => + resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context, principal }): Promise { const file = await loadActiveFile(context) const version = await loadVersion(file, input.version) @@ -309,6 +323,7 @@ async function executeRevertWorkspaceFileVersion({ const attribution = resolvePrincipalAttribution(principal, { workspaceBillingOwnerUserId: context.billedAccountUserId, }) + const commitOwner = await prepareOwnedFileContentWrite(context, content) let updated: Awaited> try { updated = await updateWorkspaceFileContent( @@ -324,6 +339,7 @@ async function executeRevertWorkspaceFileVersion({ }), expectedUpdatedAt: expectedContentAt ?? file.contentUpdatedAt ?? file.updatedAt, secretProvenancePolicy: { mode: 'reinstate', snapshot: provenance }, + commitOwner, } ) } catch (error) { @@ -354,8 +370,13 @@ async function executeRevertWorkspaceFileVersion({ */ export const revertWorkspaceFileVersion = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.revertVersion, - resolveContext: ({ input }: { input: RevertWorkspaceFileVersionInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: RevertWorkspaceFileVersionInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), execute: executeRevertWorkspaceFileVersion, projectAudit: ({ input, result }) => result.reverted @@ -388,8 +409,8 @@ export interface DeleteWorkspaceFileVersionResult { */ export const deleteWorkspaceFileVersion = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.deleteVersion, - resolveContext: ({ input }: { input: FileVersionRef }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ principal, input }: { principal: Principal; input: FileVersionRef }) => + resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context }): Promise { const file = await loadActiveFile(context) const target = await loadVersion(file, input.version) diff --git a/apps/sim/lib/workspace-files/application/owned-file-content.ts b/apps/sim/lib/workspace-files/application/owned-file-content.ts new file mode 100644 index 00000000000..a0002e8f1ec --- /dev/null +++ b/apps/sim/lib/workspace-files/application/owned-file-content.ts @@ -0,0 +1,21 @@ +import type { DbOrTx } from '@/lib/db/types' +import type { ActiveWorkspaceFileContext } from '@/lib/uploads/contexts/workspace/workspace-file-manager' + +/** + * Checks content headed for a file another resource owns before it is stored, and returns + * the step the file write runs in its transaction. A test file's source must load and declare + * one concern, and its test then records the new cases with the bytes; every other file has + * no step. + */ +export async function prepareOwnedFileContentWrite( + context: Pick, + content: Buffer +): Promise<((tx: DbOrTx) => Promise) | undefined> { + if (context.fileContext !== 'test') return undefined + const { prepareWorkflowTestSourceWrite } = await import('@/lib/workflow-tests/source-write') + return prepareWorkflowTestSourceWrite({ + fileId: context.fileId, + workspaceId: context.workspaceId, + content, + }) +} diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-artifact.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-artifact.ts index 9956025ed5b..6df08cb0f17 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-artifact.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-artifact.ts @@ -25,7 +25,11 @@ export const readWorkspaceFileArtifact = defineAuthorizedWorkspaceFileUseCase({ }: { principal: Principal input: ReadWorkspaceFileArtifactInput - }) => resolveReferencedWorkspaceFileContext(principal, input, { includeChatUploads: true }), + }) => + resolveReferencedWorkspaceFileContext(principal, input, { + includeChatUploads: true, + includeTestFiles: true, + }), async execute({ input, context, principal }) { const file = context.file const safe = await isOpaqueWorkspaceFileEgressSafe(context.workspaceId, { diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.test.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.test.ts index b885e7d1728..1b41c6e4a8d 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.test.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.test.ts @@ -78,6 +78,7 @@ describe('readWorkspaceFileContentByKey', () => { expect(mocks.getFile).toHaveBeenCalledWith(file.workspaceId, file.id, { throwOnError: true, + includeTestFiles: true, }) expect(mocks.fetchContent).toHaveBeenCalledWith(file, { maxBytes: MAX_BUFFERED_TRANSFER_BYTES, @@ -104,11 +105,12 @@ describe('readWorkspaceFileContentByKey', () => { }) ).resolves.toEqual({ file }) - expect(mocks.getMetadata).toHaveBeenCalledWith(file.key, 'workspace') - expect(mocks.loadContext).toHaveBeenCalledWith(file.id) + expect(mocks.getMetadata).toHaveBeenCalledWith(file.key) + expect(mocks.loadContext).toHaveBeenCalledWith(file.id, undefined) expect(mocks.resolvePermission).not.toHaveBeenCalled() expect(mocks.getFile).toHaveBeenCalledWith(file.workspaceId, file.id, { throwOnError: true, + includeTestFiles: true, }) expect(mocks.fetchContent).not.toHaveBeenCalled() }) diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.ts index 066c49022a2..a0da80874b5 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-content-by-key.ts @@ -1,3 +1,4 @@ +import type { Principal } from '@sim/auth/principal' import type { AuthorizedWorkspaceUseCaseContext } from '@/lib/core/application' import { OrchestrationError } from '@/lib/core/orchestration/types' import { @@ -11,6 +12,7 @@ import { getFileMetadataByKey } from '@/lib/uploads/server/metadata' import { MAX_BUFFERED_TRANSFER_BYTES } from '@/lib/uploads/shared/types' import { defineAuthorizedWorkspaceFileUseCase } from '@/lib/workspace-files/application/authorized-workspace-file-use-case' import { fileOperations } from '@/lib/workspace-files/application/operations' +import { assertOwnedFileAccess } from '@/lib/workspace-files/application/workspace-file-context' export interface ReadWorkspaceFileByKeyInput { key: string @@ -32,6 +34,7 @@ async function loadCurrentWorkspaceFileByKey( ): Promise { const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true, + includeTestFiles: true, }) if (!file || file.key !== input.key) throw new OrchestrationError('not_found', 'File not found') return file @@ -52,22 +55,32 @@ async function executeReadWorkspaceFileContentByKey({ } } +/** Workspace files and test files share the `workspace/` key prefix; the row says which. */ +const KEY_READABLE_CONTEXTS = new Set(['workspace', 'test']) + async function resolveWorkspaceFileByKeyContext({ + principal, input, }: { + principal: Principal input: ReadWorkspaceFileByKeyInput }): Promise { - const metadata = await getFileMetadataByKey(input.key, 'workspace') + const metadata = await getFileMetadataByKey(input.key) if ( !metadata?.workspaceId || + !KEY_READABLE_CONTEXTS.has(metadata.context) || (input.assertedWorkspaceId !== undefined && input.assertedWorkspaceId !== metadata.workspaceId) ) { throw new OrchestrationError('not_found', 'File not found') } - const canonical = await loadActiveWorkspaceFileContext(metadata.id) + const canonical = await loadActiveWorkspaceFileContext( + metadata.id, + metadata.context === 'test' ? { includeTestFiles: true } : undefined + ) if (!canonical || canonical.workspaceId !== metadata.workspaceId) { throw new OrchestrationError('not_found', 'File not found') } + await assertOwnedFileAccess(principal, canonical) return canonical } diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-content.test.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-content.test.ts index 6d9157cd793..a02bea328c1 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-content.test.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-content.test.ts @@ -77,11 +77,13 @@ describe('readWorkspaceFileContent', () => { expect(mocks.loadContext).toHaveBeenCalledWith('file-1', { includeDeleted: true, includeChatUploads: true, + includeTestFiles: true, }) expect(mocks.getFile).toHaveBeenCalledWith('workspace-1', 'file-1', { includeDeleted: true, throwOnError: true, includeChatUploads: true, + includeTestFiles: true, }) expect(mocks.fetchBuffer).toHaveBeenCalledWith(file, { maxBytes: 512 }) expect(mocks.getSecretProvenance).not.toHaveBeenCalled() diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-content.ts index e4216a7951f..38909345960 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-content.ts @@ -43,6 +43,7 @@ async function executeReadWorkspaceFileContent({ includeDeleted: input.includeDeleted, throwOnError: true, includeChatUploads: true, + includeTestFiles: true, }) if (!file) throw new OrchestrationError('not_found', 'File not found') const content = await fetchWorkspaceFileBuffer(file, { @@ -69,7 +70,11 @@ async function executeReadWorkspaceFileContent({ */ export const readWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readContent, - resolveContext: ({ input }) => - resolveActiveWorkspaceFileContext({ ...input, includeChatUploads: true }), + resolveContext: ({ principal, input }) => + resolveActiveWorkspaceFileContext({ + ...input, + includeChatUploads: true, + ownedFilePrincipal: principal, + }), execute: executeReadWorkspaceFileContent, }) diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-metadata.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-metadata.ts index f4c7bf8cb2f..e36226450f9 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-metadata.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-metadata.ts @@ -1,3 +1,4 @@ +import type { Principal } from '@sim/auth/principal' import type { ShareRecord } from '@/lib/api/contracts/public-shares' import type { AuthorizedWorkspaceUseCaseContext } from '@/lib/core/application' import { OrchestrationError } from '@/lib/core/orchestration/types' @@ -47,6 +48,7 @@ async function executeReadWorkspaceFileMetadata({ getWorkspaceFile(context.workspaceId, context.fileId, { includeDeleted: input.includeDeleted, throwOnError: true, + includeTestFiles: true, }), getShareForResource('file', context.fileId), ]) @@ -56,7 +58,8 @@ async function executeReadWorkspaceFileMetadata({ export const readWorkspaceFileMetadata = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readMetadata, - resolveContext: ({ input }) => resolveActiveWorkspaceFileContext(input), + resolveContext: ({ principal, input }) => + resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), execute: executeReadWorkspaceFileMetadata, }) @@ -66,12 +69,18 @@ export const readWorkspaceFileMetadata = defineAuthorizedWorkspaceFileUseCase({ */ export const readWorkspaceFileMetadataWithVersion = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readMetadata, - resolveContext: ({ input }: { input: ReadWorkspaceFileMetadataInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ReadWorkspaceFileMetadataInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context }): Promise { const [file, share] = await Promise.all([ getWorkspaceFileWithCurrentVersion(context.workspaceId, context.fileId, { includeDeleted: input.includeDeleted, + includeTestFiles: true, }), getShareForResource('file', context.fileId), ]) diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-record.test.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-record.test.ts index dae87caa171..d821e89fffd 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-record.test.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-record.test.ts @@ -59,10 +59,12 @@ describe('workspace file record reads', () => { expect(mocks.loadContext).toHaveBeenCalledWith('file-1', { includeDeleted: undefined, includeChatUploads: true, + includeTestFiles: true, }) expect(mocks.getFile).toHaveBeenCalledWith('workspace-1', 'file-1', { throwOnError: true, includeChatUploads: true, + includeTestFiles: true, }) } ) diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-record.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-record.ts index 9ffde1a241c..e7ad76f6a5f 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-record.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-record.ts @@ -1,3 +1,4 @@ +import type { Principal } from '@sim/auth/principal' import type { AuthorizedWorkspaceUseCaseContext, WorkspaceOperation } from '@/lib/core/application' import { OrchestrationError } from '@/lib/core/orchestration/types' import { @@ -26,8 +27,18 @@ export interface ReadWorkspaceFileRecordResult { function createReadWorkspaceFileRecord(operation: O) { return defineAuthorizedWorkspaceFileUseCase({ operation, - resolveContext: ({ input }: { input: ReadWorkspaceFileRecordInput }) => - resolveActiveWorkspaceFileContext({ ...input, includeChatUploads: true }), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ReadWorkspaceFileRecordInput + }) => + resolveActiveWorkspaceFileContext({ + ...input, + includeChatUploads: true, + ownedFilePrincipal: principal, + }), async execute({ context, }: AuthorizedWorkspaceUseCaseContext< @@ -38,6 +49,7 @@ function createReadWorkspaceFileRecord(opera const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true, includeChatUploads: true, + includeTestFiles: true, }) if (!file) throw new OrchestrationError('not_found', 'File not found') return { file } diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-secret-provenance.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-secret-provenance.ts index 27c8a695108..85ead6dbfdb 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-secret-provenance.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-secret-provenance.ts @@ -1,3 +1,4 @@ +import type { Principal } from '@sim/auth/principal' import { OrchestrationError } from '@/lib/core/orchestration/types' import { getWorkspaceFile } from '@/lib/uploads/contexts/workspace/workspace-file-manager' import { @@ -17,13 +18,21 @@ export interface ReadWorkspaceFileSecretProvenanceInput { export const readWorkspaceFileSecretProvenance = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readContent, - resolveContext: ({ input }: { input: ReadWorkspaceFileSecretProvenanceInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: ReadWorkspaceFileSecretProvenanceInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ input, context }): Promise<{ provenance: WorkspaceFileSecretProvenance ownerUserId: string }> { - const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true }) + const file = await getWorkspaceFile(context.workspaceId, context.fileId, { + throwOnError: true, + includeTestFiles: true, + }) if (!file) throw new OrchestrationError('not_found', 'File not found') return { provenance: await getBoundWorkspaceFileSecretProvenance(context.workspaceId, { diff --git a/apps/sim/lib/workspace-files/application/read-workspace-file-text.ts b/apps/sim/lib/workspace-files/application/read-workspace-file-text.ts index 07c66477ab5..5b972e57e5c 100644 --- a/apps/sim/lib/workspace-files/application/read-workspace-file-text.ts +++ b/apps/sim/lib/workspace-files/application/read-workspace-file-text.ts @@ -255,6 +255,9 @@ export async function parseFileText( export const readWorkspaceFileText = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.readContent, resolveContext: ({ principal, input }) => - resolveReferencedWorkspaceFileContext(principal, input, { includeChatUploads: true }), + resolveReferencedWorkspaceFileContext(principal, input, { + includeChatUploads: true, + includeTestFiles: true, + }), execute: executeReadWorkspaceFileText, }) diff --git a/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.test.ts b/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.test.ts index 1ab920d6de0..a179181cd2d 100644 --- a/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.test.ts +++ b/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.test.ts @@ -58,14 +58,30 @@ describe('workspace file reference application service', () => { expect(mocks.resolveStoredReference).toHaveBeenCalledWith( file.workspaceId, 'uploads/source.txt', - { includeChatUploads: true, chatId: 'current-chat' } + { + includeChatUploads: true, + includeTestFiles: true, + chatId: 'current-chat', + } ) expect(mocks.resolvePermission).toHaveBeenCalled() }) + it('admits a test file but never a chat upload for files.update_content', async () => { + await resolveWorkspaceFileReference({ + principal, + operation: fileOperations.updateContent, + workspaceId: 'workspace-1', + reference: 'uploads/photo.png', + }) + + expect(mocks.resolveStoredReference).toHaveBeenCalledWith('workspace-1', 'uploads/photo.png', { + includeTestFiles: true, + }) + }) + it.each([ fileOperations.rename, - fileOperations.updateContent, fileOperations.move, fileOperations.delete, fileOperations.updateShare, diff --git a/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.ts b/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.ts index 06d517477fa..35e55e4cf6e 100644 --- a/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.ts +++ b/apps/sim/lib/workspace-files/application/resolve-workspace-file-reference.ts @@ -11,6 +11,7 @@ import { } from '@/lib/uploads/contexts/workspace/workspace-file-manager' import { defineAuthorizedWorkspaceFileUseCase } from '@/lib/workspace-files/application/authorized-workspace-file-use-case' import { fileOperations } from '@/lib/workspace-files/application/operations' +import { assertOwnedFileAccess } from '@/lib/workspace-files/application/workspace-file-context' export interface ResolveWorkspaceFileReferenceInput { principal: Principal @@ -42,10 +43,18 @@ export interface ReferencedWorkspaceFileContext extends ActiveWorkspaceFileConte /** * Reads may reach a chat upload through its explicit `uploads/` reference (or its - * own id); every other file operation resolves workspace files only, so no write, move, - * rename, delete, or share can land on one. + * own id) and a test file through `tests/.test.js`; every other file operation resolves workspace files only, so no + * move, rename, delete, or share can land on any of them. */ -const CHAT_UPLOAD_LOOKUP: WorkspaceFileLookupOptions = { includeChatUploads: true } +const READ_CONTENT_LOOKUP: WorkspaceFileLookupOptions = { + includeChatUploads: true, + includeTestFiles: true, +} + +/** Content writes reach test files; no other write can. */ +const CONTENT_WRITE_LOOKUP: WorkspaceFileLookupOptions = { + includeTestFiles: true, +} /** * Resolves a VFS reference to its canonical authorization context, carrying the resolved @@ -60,13 +69,10 @@ export async function resolveReferencedWorkspaceFileContext( (principal.kind === 'delegated' && principal.serviceId === 'copilot' ? principal.resourceScope?.chatId : undefined) ?? input.chatId + const lookup = chatId === undefined ? options : { ...options, chatId } const file = input.folderId === undefined - ? await resolveStoredWorkspaceFileReference( - input.workspaceId, - input.reference, - chatId === undefined ? options : { ...options, chatId } - ) + ? await resolveStoredWorkspaceFileReference(input.workspaceId, input.reference, lookup) : await getWorkspaceFileByName(input.workspaceId, input.reference, { folderId: input.folderId, }) @@ -75,6 +81,7 @@ export async function resolveReferencedWorkspaceFileContext( if (!canonical || canonical.workspaceId !== input.workspaceId) { throw new OrchestrationError('not_found', 'File not found') } + await assertOwnedFileAccess(principal, canonical) return { ...canonical, file } } @@ -106,12 +113,13 @@ type WorkspaceFileReferenceUseCase = OperationUseCase< const workspaceFileReferenceUseCases = { [fileOperations.readContent.id]: defineWorkspaceFileReferenceUseCase( fileOperations.readContent, - CHAT_UPLOAD_LOOKUP + READ_CONTENT_LOOKUP ), [fileOperations.create.id]: defineWorkspaceFileReferenceUseCase(fileOperations.create), [fileOperations.rename.id]: defineWorkspaceFileReferenceUseCase(fileOperations.rename), [fileOperations.updateContent.id]: defineWorkspaceFileReferenceUseCase( - fileOperations.updateContent + fileOperations.updateContent, + CONTENT_WRITE_LOOKUP ), [fileOperations.move.id]: defineWorkspaceFileReferenceUseCase(fileOperations.move), [fileOperations.delete.id]: defineWorkspaceFileReferenceUseCase(fileOperations.delete), diff --git a/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts index 452ce7e9751..6385bb119b9 100644 --- a/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/update-workspace-file-content.ts @@ -15,6 +15,7 @@ import { defineAuthorizedWorkspaceFileUseCase } from '@/lib/workspace-files/appl import { parseWorkspaceFileRevision } from '@/lib/workspace-files/application/file-revision' import { resolveWorkspaceFileVersionWrite } from '@/lib/workspace-files/application/file-version-write' import { fileOperations } from '@/lib/workspace-files/application/operations' +import { prepareOwnedFileContentWrite } from '@/lib/workspace-files/application/owned-file-content' import { resolveActiveWorkspaceFileContext } from '@/lib/workspace-files/application/workspace-file-context' import { MAX_WORKSPACE_FILE_CONTENT_BYTES } from '@/lib/workspace-files/orchestration' @@ -64,6 +65,7 @@ async function updateAuthorizedWorkspaceFileContent({ const expectedUpdatedAt = input.expectedRevision ? parseWorkspaceFileRevision(input.expectedRevision, canonical.fileId) : input.expectedUpdatedAt + const commitOwner = await prepareOwnedFileContentWrite(canonical, content) let file: VersionedWorkspaceFileRecord try { file = await updateStoredWorkspaceFileContent( @@ -76,6 +78,7 @@ async function updateAuthorizedWorkspaceFileContent({ version: resolveWorkspaceFileVersionWrite(principal), ...(expectedUpdatedAt ? { expectedUpdatedAt } : {}), syncLiveDoc: input.syncLiveDoc, + commitOwner, secretProvenancePolicy: { ...(input.provenanceMode === 'preserve' ? { mode: 'preserve' as const } @@ -115,8 +118,8 @@ function projectUpdateWorkspaceFileContentAudit(result: UpdateWorkspaceFileConte const admitUpdateWorkspaceFileContentUseCase = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.updateContent, - resolveContext: ({ input }: { input: { fileId: string } }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ principal, input }: { principal: Principal; input: { fileId: string } }) => + resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute() {}, }) @@ -129,8 +132,13 @@ export async function admitUpdateWorkspaceFileContent( export const updateWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.updateContent, - resolveContext: ({ input }: { input: UpdateWorkspaceFileContentInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: UpdateWorkspaceFileContentInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), async execute({ principal, input, context }): Promise { const content = Buffer.from(input.content, input.encoding === 'base64' ? 'base64' : 'utf-8') if (content.length > MAX_WORKSPACE_FILE_CONTENT_BYTES) { @@ -151,8 +159,13 @@ export const updateWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ export const updateWorkspaceFileContentFromBuffer = defineAuthorizedWorkspaceFileUseCase({ operation: fileOperations.updateContent, - resolveContext: ({ input }: { input: UpdateWorkspaceFileContentBufferInput }) => - resolveActiveWorkspaceFileContext(input), + resolveContext: ({ + principal, + input, + }: { + principal: Principal + input: UpdateWorkspaceFileContentBufferInput + }) => resolveActiveWorkspaceFileContext({ ...input, ownedFilePrincipal: principal }), execute: ({ principal, input, context }) => updateAuthorizedWorkspaceFileContent({ principal, diff --git a/apps/sim/lib/workspace-files/application/workspace-file-context.ts b/apps/sim/lib/workspace-files/application/workspace-file-context.ts index c8eaf651197..4d6ed81c17f 100644 --- a/apps/sim/lib/workspace-files/application/workspace-file-context.ts +++ b/apps/sim/lib/workspace-files/application/workspace-file-context.ts @@ -1,3 +1,4 @@ +import type { Principal } from '@sim/auth/principal' import { OrchestrationError } from '@/lib/core/orchestration/types' import { type ActiveWorkspaceFileContext, @@ -5,6 +6,8 @@ import { loadWorkspaceFileLifecycleContext, type WorkspaceFileLifecycleContext, } from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { requireWorkflowTestsEnabled } from '@/lib/workflow-tests/feature-flag' +import { getLiveWorkflowTestByBodyFileId } from '@/lib/workflow-tests/repository' export interface WorkspaceFileContextInput { fileId: string @@ -15,6 +18,42 @@ export interface WorkspaceFileContextInput { * use cases set this: chat uploads stay out of listings and closed to writes. */ includeChatUploads?: boolean + /** + * Admit a file another resource owns — a test file (`context = 'test'`) — for this principal under its owner's policy. Only content reads and + * writes pass it; every other file operation never reaches an owned file. + */ + ownedFilePrincipal?: Principal +} + +/** + * The principals the test operations admit: people and Copilot acting for one. Workspace API + * keys, system callers, and the executor's delegation never reach a test file. + */ +function isOwnedFilePrincipal(principal: Principal): boolean { + if (principal.kind === 'delegated') return principal.serviceId === 'copilot' + return ( + principal.kind === 'session' || + principal.kind === 'personal_api_key' || + principal.kind === 'oauth_access_token' + ) +} + +/** + * A test file follows its test: it needs a live test and admits only the test operations' + * principal kinds. + */ +export async function assertOwnedFileAccess( + principal: Principal, + context: ActiveWorkspaceFileContext +): Promise { + if (context.fileContext !== 'test') return + if (!isOwnedFilePrincipal(principal)) { + throw new OrchestrationError('not_found', 'File not found') + } + if (!(await getLiveWorkflowTestByBodyFileId(context.fileId))) { + throw new OrchestrationError('not_found', 'File not found') + } + await requireWorkflowTestsEnabled(context.workspaceOrganizationId) } export async function resolveActiveWorkspaceFileContext( @@ -23,6 +62,7 @@ export async function resolveActiveWorkspaceFileContext( const canonical = await loadActiveWorkspaceFileContext(input.fileId, { includeDeleted: input.includeDeleted, includeChatUploads: input.includeChatUploads, + ...(input.ownedFilePrincipal ? { includeTestFiles: true } : {}), }) if ( !canonical || @@ -30,6 +70,7 @@ export async function resolveActiveWorkspaceFileContext( ) { throw new OrchestrationError('not_found', 'File not found') } + if (input.ownedFilePrincipal) await assertOwnedFileAccess(input.ownedFilePrincipal, canonical) return canonical } diff --git a/apps/sim/lib/workspace-files/query-scope.ts b/apps/sim/lib/workspace-files/query-scope.ts index 88c533e53f8..29069e82333 100644 --- a/apps/sim/lib/workspace-files/query-scope.ts +++ b/apps/sim/lib/workspace-files/query-scope.ts @@ -9,3 +9,9 @@ export function activeWorkspaceFileConditions(workspaceIds: string[]) { isNull(workspaceFiles.deletedAt), ] } + +/** Rows whose content the editor and Sim write: workspace files and test files. */ +export const contentWritableWorkspaceFileContextCondition = inArray(workspaceFiles.context, [ + 'workspace', + 'test', +]) diff --git a/apps/sim/package.json b/apps/sim/package.json index 1a8f53bc0dc..c8af6ae60ba 100644 --- a/apps/sim/package.json +++ b/apps/sim/package.json @@ -296,6 +296,8 @@ "@types/ssh2": "^1.15.5", "@vitejs/plugin-react": "^6.0.5", "@vitest/coverage-v8": "^5.0.1", + "@vitest/expect": "5.0.1", + "@vitest/spy": "5.0.1", "artillery": "2.0.34", "node-gyp": "12.4.0", "postcss": "^8", diff --git a/apps/sim/providers/runtime-context.ts b/apps/sim/providers/runtime-context.ts index aaef60250ac..5b7459e35d6 100644 --- a/apps/sim/providers/runtime-context.ts +++ b/apps/sim/providers/runtime-context.ts @@ -1,7 +1,7 @@ import { AsyncLocalStorage } from 'node:async_hooks' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { isRecordLike, omit } from '@sim/utils/object' +import { isRecordLike, omit, toRecord, toRecordOrNull } from '@sim/utils/object' import { durableSecretProvenanceFromRegistry, importDurableSecretProvenance, @@ -17,11 +17,15 @@ import { CHILD_EXECUTION_ID_OUTPUT_KEY, CHILD_TRACE_DISABLED_OUTPUT_KEY, } from '@/executor/constants' +import type { ExecutionTestHooks, MockedToolCall } from '@/executor/execution/types' import type { ExecutionContext } from '@/executor/types' +import { projectResolvedSecretModelContent } from '@/executor/utils/resolved-secret-content-projection' import type { ResolvedSecretTraceRegistry } from '@/executor/utils/resolved-secret-trace-registry' +import { stripCloneSuffixes } from '@/executor/utils/subflow-node-id-codec' import { getPreparedProviderToolInputProvenance } from '@/providers/tool-input-provenance' import type { ProviderId } from '@/providers/types' import { type ExecuteToolOptions, executeTool } from '@/tools' +import { getToolParams } from '@/tools/metadata' import type { ToolResponse } from '@/tools/types' export interface ProviderRuntimeContext { @@ -124,6 +128,41 @@ function accumulateFailedFunctionToolCost( } } +/** The arguments a model sent a tool: no internal context, credentials, or fixed configuration. */ +function modelSuppliedToolInput( + toolId: string, + params: Record +): Record { + const declared = getToolParams(toolId) + // MCP and custom tools declare their arguments only in the schema the model was given. + const dynamicArgs = declared + ? undefined + : toRecord(toRecordOrNull(params._toolSchema)?.properties ?? {}) + const input: Record = {} + for (const [key, value] of Object.entries(params)) { + const visibility = declared?.[key]?.visibility + const isArgument = declared + ? Object.hasOwn(declared, key) + : Object.hasOwn(dynamicArgs ?? {}, key) + if (isArgument && visibility !== 'user-only' && visibility !== 'hidden') input[key] = value + } + return input +} + +/** A test's answer to a tool call; a rejected mock becomes the tool's failure. */ +async function resolveMockedTool( + testHooks: ExecutionTestHooks, + call: MockedToolCall, + abortSignal: AbortSignal | undefined +): Promise { + try { + return { success: true, output: await testHooks.resolveToolMock(call, abortSignal) } + } catch (error) { + if (abortSignal?.aborted) throw error + return { success: false, output: {}, error: getErrorMessage(error) } + } +} + export async function executeProviderTool( toolId: string, params: Parameters[1], @@ -224,6 +263,36 @@ export async function executeProviderTool( : registry.forkForToolCall() : undefined + const callerContext = options.executionContext ?? runtimeContext?.executionContext + const testHooks = callerContext?.testHooks + const callerBlockId = + isRecordLike(params._context) && typeof params._context.blockId === 'string' + ? stripCloneSuffixes(params._context.blockId) + : undefined + if ( + testHooks && + callerBlockId && + !memoryRetrieval && + testHooks.mocksTool(callerBlockId, executionToolId) + ) { + const input = projectResolvedSecretModelContent( + modelSuppliedToolInput(executionToolId, params), + registry + ) + const response: ToolResponse = input.safe + ? await resolveMockedTool( + testHooks, + { blockId: callerBlockId, toolId: executionToolId, input: toRecord(input.value) }, + callerContext?.abortSignal + ) + : { + success: false, + output: {}, + error: `Mocked tool "${executionToolId}" has inputs whose secrets cannot be redacted`, + } + return { rawResponse: response, modelResponse: response } + } + try { const executionContext = options.executionContext ?? runtimeContext?.executionContext const result = memoryRetrieval diff --git a/apps/sim/sandbox-tasks/index.ts b/apps/sim/sandbox-tasks/index.ts index 3fab0599a5a..0ec96809128 100644 --- a/apps/sim/sandbox-tasks/index.ts +++ b/apps/sim/sandbox-tasks/index.ts @@ -12,6 +12,8 @@ * - `pptx-generate` → `apps/sim/sandbox-tasks/pptx-generate.ts` * - `docx-generate` → `apps/sim/sandbox-tasks/docx-generate.ts` * - `pdf-generate` → `apps/sim/sandbox-tasks/pdf-generate.ts` + * - `workflow-test-run`, `workflow-test-collect` → `apps/sim/sandbox-tasks/workflow-test.ts` + * (user code is an ES module importing only `vitest` and `sim:test`) * * Tier 2 — Raw isolated-vm consumers. * Value-producing executions where the user supplies arbitrary JS and the diff --git a/apps/sim/sandbox-tasks/registry.ts b/apps/sim/sandbox-tasks/registry.ts index 3866739764d..3df98e26467 100644 --- a/apps/sim/sandbox-tasks/registry.ts +++ b/apps/sim/sandbox-tasks/registry.ts @@ -2,6 +2,7 @@ import type { SandboxTask } from '@/lib/execution/sandbox/types' import { docxGenerateTask } from '@/sandbox-tasks/docx-generate' import { pdfGenerateTask } from '@/sandbox-tasks/pdf-generate' import { pptxGenerateTask } from '@/sandbox-tasks/pptx-generate' +import { workflowTestCollectTask, workflowTestRunTask } from '@/sandbox-tasks/workflow-test' /** * Every piece of user code that runs inside the isolated-vm sandbox is defined @@ -12,6 +13,8 @@ export const SANDBOX_TASKS = { 'pptx-generate': pptxGenerateTask, 'docx-generate': docxGenerateTask, 'pdf-generate': pdfGenerateTask, + 'workflow-test-run': workflowTestRunTask, + 'workflow-test-collect': workflowTestCollectTask, } as const satisfies Record export type SandboxTaskId = keyof typeof SANDBOX_TASKS diff --git a/apps/sim/sandbox-tasks/workflow-test.ts b/apps/sim/sandbox-tasks/workflow-test.ts new file mode 100644 index 00000000000..24f9915275d --- /dev/null +++ b/apps/sim/sandbox-tasks/workflow-test.ts @@ -0,0 +1,543 @@ +import { defineSandboxTask } from '@/lib/execution/sandbox/define-task' +import type { SandboxTaskInput } from '@/lib/execution/sandbox/types' +import { workflowTestBrokers } from '@/lib/workflow-tests/brokers' + +/** Wall-clock budget for one test file: every test, workflow run, and judge call in it. */ +const WORKFLOW_TEST_FILE_TIMEOUT_MS = 15 * 60_000 +const DEFAULT_TEST_TIMEOUT_MS = 5 * 60_000 +const MAX_RUBRIC_RUNS = 5 + +/** + * The vitest-shaped harness. User code is a test file evaluated as an ES module; its + * `import ... from 'vitest'` and `'sim:test'` resolve to `WORKFLOW_TEST_MODULES`, which read + * `globalThis.__simTest`. Module evaluation only registers suites and tests; `finalize` + * runs them (or, for collection, just lists them) and returns the report as JSON bytes. + */ +const HARNESS = ` + const vx = globalThis.__bundles['vitest-expect']; + if (!vx) throw new Error('vitest-expect bundle not loaded'); + const brokers = globalThis.__brokers; + const call = (name, args) => brokers[name](args); + + const DEFAULT_TEST_TIMEOUT_MS = ${DEFAULT_TEST_TIMEOUT_MS}; + const MAX_RUBRIC_RUNS = ${MAX_RUBRIC_RUNS}; + + const newSuite = (name, parent, mode) => ({ + name, parent, mode, children: [], + hooks: { beforeAll: [], afterAll: [], beforeEach: [], afterEach: [] }, + }); + const root = newSuite('', null, 'run'); + let currentSuite = root; + let collecting = true; + let currentTest = null; + let hasOnly = false; + + const requireCollecting = (what) => { + if (!collecting) throw new Error(what + ' must be called at the top level of the file or inside describe()'); + }; + const requireRunning = (what) => { + if (collecting) throw new Error(what + ' can only be called inside a test or a hook'); + }; + const requireName = (what, name) => { + if (typeof name !== 'string' || name.trim() === '') throw new Error(what + ' needs a name'); + }; + + function defineSuite(mode) { + return function describe(name, fn) { + requireCollecting('describe()'); + requireName('describe()', name); + if (typeof fn !== 'function') throw new Error('describe("' + name + '") needs a callback'); + if (mode === 'only') hasOnly = true; + const suite = newSuite(name, currentSuite, mode); + currentSuite.children.push({ kind: 'suite', suite }); + const previous = currentSuite; + currentSuite = suite; + try { + const result = fn(); + if (result && typeof result.then === 'function') { + throw new Error('describe("' + name + '") callback must not be async; put awaits inside tests or hooks'); + } + } finally { + currentSuite = previous; + } + }; + } + function defineTest(mode) { + return function it(name, fn, timeoutMs) { + requireCollecting('it()'); + requireName('it()', name); + if (mode !== 'skip' && typeof fn !== 'function') throw new Error('it("' + name + '") needs a callback'); + if (timeoutMs !== undefined && !(Number.isInteger(timeoutMs) && timeoutMs > 0)) { + throw new Error('it("' + name + '") timeout must be a positive whole number of milliseconds'); + } + if (mode === 'only') hasOnly = true; + const site = callSite(new Error().stack); + if (!site) throw new Error('it("' + name + '") could not be located in the file'); + const span = typeof fn === 'function' ? String(fn).split('\\n').length - 1 : 0; + currentSuite.children.push({ kind: 'test', test: { name, fn, mode, timeoutMs: timeoutMs ?? DEFAULT_TEST_TIMEOUT_MS, line: site.line, endLine: site.line + span } }); + }; + } + const describe = defineSuite('run'); + describe.only = defineSuite('only'); + describe.skip = defineSuite('skip'); + const it = defineTest('run'); + it.only = defineTest('only'); + it.skip = defineTest('skip'); + const hook = (kind) => (fn) => { + requireCollecting(kind + '()'); + if (typeof fn !== 'function') throw new Error(kind + '() needs a callback'); + currentSuite.hooks[kind].push(fn); + }; + + function callSite(stack) { + for (const line of String(stack || '').split('\\n').slice(1)) { + const match = /\\(?([^\\s()]+):(\\d+):(\\d+)\\)?\\s*$/.exec(line.trim()); + if (match && !match[1].startsWith('sandbox/')) return { line: Number(match[2]), column: Number(match[3]) }; + } + return null; + } + function describeError(error) { + const info = { message: error && error.message ? String(error.message) : String(error) }; + if (error && error.showDiff !== false && error && 'expected' in error && 'actual' in error) { + for (const key of ['actual', 'expected']) { + try { + JSON.stringify(error[key]); + info[key] = error[key]; + } catch {} + } + } + return info; + } + + const baseExpect = vx.createExpect(); + function settle(check, status, error) { + if (check.status !== 'pending') return; + check.status = status; + if (error) Object.assign(check, describeError(error)); + } + function track(assertion, check, test) { + return new Proxy(assertion, { + get(target, key) { + let value; + try { + value = Reflect.get(target, key, target); + } catch (error) { + settle(check, 'fail', error); + throw error; + } + if (typeof value === 'function') { + return (...args) => { + let result; + try { + result = value.apply(target, args); + } catch (error) { + settle(check, 'fail', error); + throw error; + } + if (result && typeof result.then === 'function') { + const settled = result.then( + (resolved) => { settle(check, 'pass'); return resolved; }, + (error) => { settle(check, 'fail', error); throw error; } + ); + test.pendingAssertions.push(settled.catch(() => {})); + return settled; + } + settle(check, 'pass'); + return result && typeof result === 'object' && result.__flags ? track(result, check, test) : result; + }; + } + if (value && typeof value === 'object' && value.__flags) return track(value, check, test); + return value; + }, + }); + } + const expect = (value, message) => { + const assertion = baseExpect(value, message); + if (!currentTest) return assertion; + const check = { status: 'pending', ...(callSite(new Error().stack) || {}) }; + currentTest.checks.push(check); + currentTest.activeCheck = check; + return track(assertion, check, currentTest); + }; + Object.setPrototypeOf(expect, baseExpect); + expect.assertions = (count) => { + if (!currentTest) throw new Error('expect.assertions() can only be called inside a test'); + if (!Number.isInteger(count) || count < 0) throw new Error('expect.assertions() needs a whole number'); + currentTest.expectedAssertions = count; + }; + expect.hasAssertions = () => { + if (!currentTest) throw new Error('expect.hasAssertions() can only be called inside a test'); + currentTest.expectsAssertions = true; + }; + function checkAssertionCount(test) { + const made = test.checks.length; + if (test.expectedAssertions !== null && made !== test.expectedAssertions) { + throw new Error('Expected ' + test.expectedAssertions + ' assertions to be called but received ' + made); + } + if (test.expectsAssertions && made === 0) throw new Error('Expected at least one assertion to be called but received none'); + } + + baseExpect.extend({ + async toMatchRubric(received, rubric, options) { + if (typeof rubric !== 'string' || rubric.trim() === '') throw new Error('toMatchRubric() needs a rubric string'); + const runs = options && options.runs !== undefined ? options.runs : 1; + if (!Number.isInteger(runs) || runs < 1 || runs > MAX_RUBRIC_RUNS) { + throw new Error('toMatchRubric() runs must be a whole number from 1 to ' + MAX_RUBRIC_RUNS); + } + const value = typeof received === 'string' ? received : JSON.stringify(received, null, 2); + if (value === undefined) throw new Error('toMatchRubric() received undefined'); + const check = currentTest && currentTest.activeCheck; + let verdict = { pass: true, reason: '' }; + for (let i = 0; i < runs && verdict.pass; i++) { + verdict = await call('testJudge', { value, rubric }); + } + if (check) check.judge = verdict.reason; + return { + pass: verdict.pass, + message: () => verdict.pass ? 'Expected the output not to satisfy: ' + rubric + '. ' + verdict.reason : verdict.reason, + }; + }, + }); + + const vi = { fn: vx.fn, spyOn: vx.spyOn, isMockFunction: vx.isMockFunction }; + + const registrations = new Map(); + const registrationKey = (kind, workflow, block, tool, mcpServer, customTool) => kind + '\\u0000' + (workflow ?? '') + '\\u0000' + (block ?? '') + '\\u0000' + (mcpServer ?? '') + '\\u0000' + (customTool ? 'custom' : '') + '\\u0000' + (tool ?? ''); + const describeTool = (r) => r.mcpServer !== null + ? '{ mcp: "' + r.mcpServer + '", tool: "' + r.tool + '" }' + : r.customTool ? '{ customTool: "' + r.tool + '" }' : '"' + r.tool + '"'; + const describeRegistration = (r) => r.kind === 'tool' + ? 'mockTool(' + (r.block ? '"' + r.block + '", ' : '') + describeTool(r) + ')' + : (r.kind === 'mock' ? 'mockBlock' : 'spyOnBlock') + '("' + (r.workflow ? r.workflow + '", "' : '') + r.block + '")'; + function addRegistration(entry) { + const key = registrationKey(entry.kind, entry.workflow, entry.block, entry.tool, entry.mcpServer, entry.customTool); + const existing = registrations.get(key); + if (existing) return existing.fn; + const fn = vx.fn().mockName(entry.label); + const registration = { ...entry, key, fn, test: currentTest, sample: undefined }; + if (entry.kind !== 'spy') { + fn.mockSampleOutput = (overrides) => { + if (overrides !== undefined && (overrides === null || typeof overrides !== 'object' || Array.isArray(overrides))) { + throw new Error(describeRegistration(registration) + '.mockSampleOutput() takes an object of fields to override'); + } + registration.sample = overrides ?? {}; + return fn; + }; + } + registrations.set(key, registration); + return fn; + } + function register(kind, first, second) { + const workflow = second === undefined ? null : first; + const block = second === undefined ? first : second; + const what = kind === 'mock' ? 'mockBlock()' : 'spyOnBlock()'; + requireName(what, block); + if (workflow !== null) requireName(what + ' workflow', workflow); + return addRegistration({ kind, workflow, block, tool: null, mcpServer: null, customTool: false, label: workflow ? workflow + ' / ' + block : block }); + } + const mockBlock = (first, second) => register('mock', first, second); + const spyOnBlock = (first, second) => register('spy', first, second); + const mockTool = (first, second) => { + const block = second === undefined ? null : first; + const target = second === undefined ? first : second; + if (block !== null) requireName('mockTool() Agent block', block); + const objectForms = 'an MCP tool as { mcp: "Server name", tool: "tool_name" } or a custom tool as { customTool: "Title" }'; + let tool = target; + let mcpServer = null; + let customTool = false; + if (target !== null && typeof target === 'object') { + const keys = Object.keys(target).sort().join(','); + if (keys === 'mcp,tool') { + requireName('mockTool() MCP server', target.mcp); + requireName('mockTool() MCP tool', target.tool); + mcpServer = target.mcp; + tool = target.tool; + } else if (keys === 'customTool') { + requireName('mockTool() custom tool', target.customTool); + customTool = true; + tool = target.customTool; + } else { + throw new Error('mockTool() takes a built-in tool id, ' + objectForms); + } + } else { + requireName('mockTool()', tool); + if (tool.startsWith('mcp-') || tool.startsWith('custom_')) throw new Error('mockTool("' + tool + '"): name ' + objectForms); + } + const name = mcpServer !== null ? mcpServer + ' / ' + tool : tool; + return addRegistration({ kind: 'tool', workflow: null, block, tool, mcpServer, customTool, label: block ? block + ' / ' + name : name }); + }; + + /** Overrides on a generated sample: objects merge, anything else replaces, and an unknown field is a mistake. */ + function mergeSample(sample, overrides, path) { + const merged = { ...sample }; + for (const [field, value] of Object.entries(overrides)) { + const at = path ? path + '.' + field : field; + if (!(field in sample)) { + throw new Error('mockSampleOutput(): the output has no field "' + at + '". It has: ' + (Object.keys(sample).join(', ') || 'no fields')); + } + const base = sample[field]; + merged[field] = base && typeof base === 'object' && !Array.isArray(base) && value && typeof value === 'object' && !Array.isArray(value) + ? mergeSample(base, value, at) + : value; + } + return merged; + } + + class WorkflowRunError extends Error { + constructor(message, executionId, output) { + super(message); + this.name = 'WorkflowRunError'; + this.executionId = executionId; + this.output = output; + } + } + + async function answerMock(event) { + const entry = registrations.get(event.key); + if (!entry) return { error: 'No mock is registered for ' + (event.tool ?? event.block) }; + try { + if (entry.sample !== undefined) { + let output; + try { + output = mergeSample(event.sample, entry.sample, ''); + } catch (error) { + return { error: String(error.message), mistake: describeRegistration(entry) + ': ' + String(error.message) }; + } + await entry.fn(event.input); + return { output }; + } + const output = await entry.fn(event.input); + if (output === undefined) { + return { error: describeRegistration(entry) + ' returned nothing. Give it a value with .mockResolvedValue(...) or .mockSampleOutput()' }; + } + return { output }; + } catch (error) { + return { error: error && error.message ? String(error.message) : String(error) }; + } + } + + async function runWorkflow(workflow, input, options) { + requireRunning('runWorkflow()'); + if (!currentTest) throw new Error('runWorkflow() runs only inside a test, beforeEach, or afterEach; beforeAll and afterAll have no test to record it on'); + requireName('runWorkflow()', workflow); + if (options !== undefined) { + const extra = options === null || typeof options !== 'object' ? ['options'] : Object.keys(options).filter((key) => key !== 'trigger'); + if (extra.length > 0) throw new Error('runWorkflow() options take only { trigger: "Trigger block name" }; pick draft or deployed when you run the tests'); + requireName('runWorkflow() trigger', options.trigger); + } + const trigger = options ? options.trigger : null; + const targets = [...registrations.values()].map((r) => r.kind === 'tool' + ? { key: r.key, kind: 'tool', workflow: null, block: r.block, tool: r.tool, mcpServer: r.mcpServer, customTool: r.customTool } + : { key: r.key, kind: r.kind, workflow: r.workflow, block: r.block }); + const test = currentTest; + const mistakes = []; + let event = await call('testStart', { workflow, trigger, input: input === undefined ? null : input, targets }); + test.runIds.push(event.runId); + while (event.kind !== 'done') { + if (event.kind === 'mock') { + const { mistake, ...reply } = await answerMock(event); + if (mistake) mistakes.push(mistake); + event = await call('testReply', { runId: event.runId, callId: event.callId, ...reply }); + } else { + event = await call('testNext', { runId: event.runId }); + } + } + test.executions.push({ workflow, executionId: event.executionId }); + for (const key of event.matchedKeys) test.matchedKeys.add(key); + for (const spyCall of event.spyCalls) { + const entry = registrations.get(spyCall.key); + if (!entry) continue; + entry.fn.mockImplementationOnce(() => spyCall.output); + entry.fn(spyCall.input); + } + if (mistakes.length > 0) throw new Error(mistakes.join('\\n')); + if (event.error) throw new WorkflowRunError(event.error, event.executionId, event.output); + return { output: event.output, executionId: event.executionId }; + } + + const logLines = []; + for (const level of ['log', 'info', 'warn', 'error']) { + const original = console[level]; + console[level] = (...args) => { + if (currentTest) { + currentTest.logs.push(args.map((a) => typeof a === 'string' ? a : (() => { try { return JSON.stringify(a); } catch { return String(a); } })()).join(' ')); + } + return original.apply(console, args); + }; + } + + /** A timed-out case keeps running in the isolate, so every later case is reported unrun. */ + let timedOutTest = null; + function withTimeout(promise, ms, label, onTimeout) { + let timer; + return Promise.race([ + promise, + new Promise((_, reject) => { + timer = setTimeout(() => { + onTimeout(); + reject(new Error(label + ' timed out after ' + ms + 'ms')); + }, ms); + }), + ]).finally(() => clearTimeout(timer)); + } + + const suitePath = (suite) => { + const names = []; + for (let s = suite; s && s.parent; s = s.parent) names.unshift(s.name); + return names; + }; + const suiteChain = (suite) => { + const chain = []; + for (let s = suite; s; s = s.parent) chain.unshift(s); + return chain; + }; + function listTests(suite, out) { + for (const child of suite.children) { + if (child.kind === 'suite') listTests(child.suite, out); + else out.push({ suite, test: child.test, path: [...suitePath(suite), child.test.name] }); + } + return out; + } + + async function runHooks(fns) { + for (const fn of fns) await fn(); + } + + function willRun(suite, test, onlyNames) { + const chain = suiteChain(suite); + if (test.mode === 'skip' || chain.some((s) => s.mode === 'skip')) return false; + if (hasOnly && test.mode !== 'only' && !chain.some((s) => s.mode === 'only')) return false; + return !onlyNames || onlyNames.includes([...suitePath(suite), test.name].join(' > ')); + } + + async function runOne(suite, test, onlyNames) { + const path = [...suitePath(suite), test.name]; + const result = { path, status: 'pass', durationMs: 0, checks: [], executions: [], logs: [], runIds: [], matchedKeys: new Set(), activeCheck: null, expectedAssertions: null, expectsAssertions: false, pendingAssertions: [] }; + if (!willRun(suite, test, onlyNames)) { + result.status = 'skip'; + return result; + } + if (timedOutTest) { + result.status = 'fail'; + result.error = { name: 'Error', message: 'Not run: "' + timedOutTest + '" timed out and may still be running' }; + await call('testProgress', { path, status: result.status }); + return result; + } + await call('testProgress', { path, status: 'running' }); + const startedAt = Date.now(); + currentTest = result; + try { + await withTimeout((async () => { + for (const s of suiteChain(suite)) await runHooks(s.hooks.beforeEach); + try { + await test.fn(); + } finally { + for (const s of suiteChain(suite).reverse()) await runHooks(s.hooks.afterEach); + } + await Promise.all(result.pendingAssertions); + if (result.checks.some((check) => check.status === 'fail')) { + throw new Error('An assertion failed after its test returned; await expect(...).resolves, .rejects, and toMatchRubric'); + } + checkAssertionCount(result); + const unmatched = [...registrations.values()].filter((r) => r.test === result && result.runIds.length > 0 && !result.matchedKeys.has(r.key)); + if (unmatched.length > 0) { + const names = unmatched.map(describeRegistration); + throw new Error(names.join(', ') + ' matched no block in the workflows this test ran'); + } + })(), test.timeoutMs, 'Test', () => { timedOutTest = path.join(' > '); }); + } catch (error) { + result.status = 'fail'; + const owned = result.checks.find((check) => check.status === 'fail'); + if (!owned) result.error = { ...describeError(error), ...(callSite(error && error.stack) || {}) }; + } finally { + for (const check of result.checks) if (check.status === 'pending') check.status = 'skip'; + for (const [key, registration] of registrations) if (registration.test === result) registrations.delete(key); + currentTest = null; + if (result.runIds.length) await call('testCancel', { runIds: result.runIds }); + result.durationMs = Date.now() - startedAt; + } + await call('testProgress', { path, status: result.status }); + return result; + } + + async function runSuiteTree(suite, onlyNames, out) { + const anyRuns = listTests(suite, []).some((entry) => willRun(entry.suite, entry.test, onlyNames)); + if (anyRuns) await runHooks(suite.hooks.beforeAll); + try { + for (const child of suite.children) { + if (child.kind === 'suite') await runSuiteTree(child.suite, onlyNames, out); + else out.push(await runOne(suite, child.test, onlyNames)); + } + } finally { + if (anyRuns) await runHooks(suite.hooks.afterAll); + } + } + + globalThis.__simTest = { + modules: { + vitest: { describe, it, test: it, expect, vi, beforeAll: hook('beforeAll'), afterAll: hook('afterAll'), beforeEach: hook('beforeEach'), afterEach: hook('afterEach') }, + sim: { runWorkflow, mockBlock, spyOnBlock, mockTool }, + }, + collect() { + collecting = false; + return { + topLevel: root.children.map((c) => c.kind === 'suite' ? { kind: 'suite', name: c.suite.name } : { kind: 'test', name: c.test.name }), + tests: listTests(root, []).map((e) => ({ path: e.path, mode: e.test.mode, line: e.test.line, endLine: e.test.endLine })), + }; + }, + async run() { + const plan = this.collect(); + const { only } = await call('testPlan', {}); + if (only) { + const known = new Set(plan.tests.map((t) => t.path.join(' > '))); + const unknown = only.filter((name) => !known.has(name)); + if (unknown.length > 0) throw new Error('No test is named ' + unknown.map((name) => '"' + name + '"').join(', ') + ' in this file'); + } + const results = []; + await runSuiteTree(root, only, results); + return { + topLevel: plan.topLevel, + tests: results.map(({ path, status, durationMs, checks, executions, logs, error }) => ({ path, status, durationMs, checks, executions, logs, ...(error ? { error } : {}) })), + }; + }, + }; +` + +const VITEST_MODULE = ` +const m = globalThis.__simTest.modules.vitest; +export const { describe, it, test, expect, vi, beforeAll, afterAll, beforeEach, afterEach } = m; +` + +const SIM_TEST_MODULE = ` +const m = globalThis.__simTest.modules.sim; +export const { runWorkflow, mockBlock, spyOnBlock, mockTool } = m; +` + +const WORKFLOW_TEST_MODULES = { vitest: VITEST_MODULE, 'sim:test': SIM_TEST_MODULE } + +const encodeReport = (expression: string) => + `return new TextEncoder().encode(JSON.stringify(${expression}));` + +/** Runs every test in a file (or only `testPlan().only`) and returns the report. */ +export const workflowTestRunTask = defineSandboxTask({ + id: 'workflow-test-run', + timeoutMs: WORKFLOW_TEST_FILE_TIMEOUT_MS, + bundles: ['vitest-expect'], + brokers: workflowTestBrokers, + bootstrap: HARNESS, + userModules: WORKFLOW_TEST_MODULES, + finalize: encodeReport('await globalThis.__simTest.run()'), + toResult: (bytes) => Buffer.from(bytes), +}) + +/** Evaluates a test file without running anything and lists its suites and tests. */ +export const workflowTestCollectTask = defineSandboxTask({ + id: 'workflow-test-collect', + timeoutMs: 10_000, + bundles: ['vitest-expect'], + brokers: [], + bootstrap: HARNESS, + userModules: WORKFLOW_TEST_MODULES, + finalize: encodeReport('globalThis.__simTest.collect()'), + toResult: (bytes) => Buffer.from(bytes), +}) diff --git a/apps/sim/stores/logs/filters/types.ts b/apps/sim/stores/logs/filters/types.ts index c2f8c874968..a5414fe6036 100644 --- a/apps/sim/stores/logs/filters/types.ts +++ b/apps/sim/stores/logs/filters/types.ts @@ -32,6 +32,7 @@ export const CORE_TRIGGER_TYPES = [ 'mothership', 'workflow', 'custom_block', + 'test', ] as const export type CoreTriggerType = (typeof CORE_TRIGGER_TYPES)[number] diff --git a/apps/sim/stores/workflow-tests/store.ts b/apps/sim/stores/workflow-tests/store.ts new file mode 100644 index 00000000000..81cd81d5235 --- /dev/null +++ b/apps/sim/stores/workflow-tests/store.ts @@ -0,0 +1,41 @@ +import { create } from 'zustand' +import { devtools } from 'zustand/middleware' + +/** + * Which run each open test shows. The run picker and the results it controls render in + * different places (the page header and its preview, or a chat tab's actions and its + * content), so the choice lives here. A missing entry means the latest run. + */ +interface TestRunSelectionState { + selectedRunIds: Record + /** Tests whose open editor holds edits the server has not saved; Run waits for them. */ + unsavedTests: Record + selectRun: (testKey: string, runId: string | null) => void + setUnsaved: (testKey: string, unsaved: boolean) => void + reset: () => void +} + +export const useTestRunSelectionStore = create()( + devtools( + (set) => ({ + selectedRunIds: {}, + unsavedTests: {}, + selectRun: (testKey, runId) => + set((state) => { + const { [testKey]: _previous, ...rest } = state.selectedRunIds + return { selectedRunIds: runId === null ? rest : { ...rest, [testKey]: runId } } + }), + setUnsaved: (testKey, unsaved) => + set((state) => { + const { [testKey]: _previous, ...rest } = state.unsavedTests + return { unsavedTests: unsaved ? { ...rest, [testKey]: true } : rest } + }), + reset: () => set({ selectedRunIds: {}, unsavedTests: {} }), + }), + { name: 'test-run-selection' } + ) +) + +export function testRunSelectionKey(workspaceId: string, name: string): string { + return `${workspaceId}:${name}` +} diff --git a/bun.lock b/bun.lock index cfd373b2720..eaa1e8600c9 100644 --- a/bun.lock +++ b/bun.lock @@ -419,6 +419,8 @@ "@types/ssh2": "^1.15.5", "@vitejs/plugin-react": "^6.0.5", "@vitest/coverage-v8": "^5.0.1", + "@vitest/expect": "5.0.1", + "@vitest/spy": "5.0.1", "artillery": "2.0.34", "node-gyp": "12.4.0", "postcss": "^8", @@ -2084,7 +2086,7 @@ "@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="], - "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], @@ -2462,14 +2464,20 @@ "@vitest/coverage-v8": ["@vitest/coverage-v8@5.0.1", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/istanbul-lib-coverage": "^1.0.0", "@vitest/istanbul-lib-report": "^1.0.0", "ast-v8-to-istanbul": "^1.0.5", "magicast": "^0.5.4", "obug": "^2.1.4", "std-env": "^4.2.0", "tinyrainbow": "^3.1.1" }, "peerDependencies": { "@vitest/browser": "5.0.1", "vitest": "5.0.1" }, "optionalPeers": ["@vitest/browser"] }, "sha512-FRC8ACiudC3dI6MTplzRSYWHDRnIv2IPfbzs4FdoJNsMal/35sWV8hwIfV8ZcqzSPy+uXHeMVONt9CEqtOU17w=="], + "@vitest/expect": ["@vitest/expect@5.0.1", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "5.0.1", "@vitest/utils": "5.0.1", "chai": "^6.2.2", "tinyrainbow": "^3.1.1" } }, "sha512-U4YclNr7ds8kqZdtLcoVCYTaLRmaKeyjRjQHQ2We5iY0ZjegavIeBWJOr2F4pgUFuN0tO0bz/cVa4ERwKhf1Hg=="], + "@vitest/istanbul-lib-coverage": ["@vitest/istanbul-lib-coverage@1.0.1", "", {}, "sha512-k3DJZ8LhMBK9NS4SclF1ASD3OgXEWDorbIcPTRDK0/Zae6fRvu+fJRxtFdLfHsa9Y24beCdPnoNZ4LviTNstfA=="], "@vitest/istanbul-lib-report": ["@vitest/istanbul-lib-report@1.0.1", "", { "dependencies": { "@vitest/istanbul-lib-coverage": "1.0.1" } }, "sha512-1EOLRfsTMnyAr3+kEAsP4o9dhaDlGPpD7H5iLBBeq//YpNB1VIahkPhB+eRp9N2Dkfw8oySROjE3yf9XDeaIkQ=="], "@vitest/mocker": ["@vitest/mocker@5.0.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.31", "@vitest/spy": "5.0.1", "estree-walker": "^3.0.3", "magic-string": "^1.2.3" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q=="], + "@vitest/pretty-format": ["@vitest/pretty-format@5.0.1", "", { "dependencies": { "tinyrainbow": "^3.1.1" } }, "sha512-6guWwj5d9bguuefTOvJoq387tfpkzSv554YdUEGzjJH2PnnmvzTLQ1UQSuAk5wBFVhf2CUmy/S/palOcb6dmpA=="], + "@vitest/spy": ["@vitest/spy@5.0.1", "", {}, "sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q=="], + "@vitest/utils": ["@vitest/utils@5.0.1", "", { "dependencies": { "@vitest/pretty-format": "5.0.1", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.1" } }, "sha512-E9+yEA+jsfaoxZcUHFzEqUrQcoNh2EwrPT5efIqkUPUwD5Ua2Li9BRWaYeRwvzvdLgTSVrre8oKNeyrfg7KkdQ=="], + "@xmldom/is-dom-node": ["@xmldom/is-dom-node@1.0.1", "", {}, "sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q=="], "@xmldom/xmldom": ["@xmldom/xmldom@0.8.13", "", {}, "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw=="], @@ -2780,6 +2788,8 @@ "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], "cookie-parser": ["cookie-parser@1.4.7", "", { "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.6" } }, "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw=="], @@ -4858,8 +4868,6 @@ "@a2a-js/sdk/jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], - "@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@antfu/install-pkg/package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], "@antfu/install-pkg/tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], @@ -5042,8 +5050,6 @@ "@better-auth/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.41.1", "", {}, "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA=="], - "@better-auth/core/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@better-auth/core/jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], "@better-auth/oauth-provider/jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], @@ -5512,8 +5518,6 @@ "echarts/tslib": ["tslib@2.3.0", "", {}, "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="], - "effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "electron/@types/node": ["@types/node@24.13.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q=="], "electron-builder/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -5556,8 +5560,6 @@ "framer-motion/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "fumadocs-mdx/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "fumadocs-mdx/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "fumadocs-mdx/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], @@ -5666,6 +5668,8 @@ "node-gyp/undici": ["undici@6.28.1", "", {}, "sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA=="], + "nuqs/@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "nypm/tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], "open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], diff --git a/knip.jsonc b/knip.jsonc index 513ca123fc5..ad3883359dc 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -66,6 +66,8 @@ "node-gyp", // Imported inside the generated source string in sandbox/bundles/build.ts. "pptxgenjs", + "@vitest/expect", + "@vitest/spy", // Next aliases this marker to its bundled server-only module. "server-only", // Knip's email CLI plugin infers this transitive preview implementation. diff --git a/packages/db/migrations/0403_workflow_tests.sql b/packages/db/migrations/0403_workflow_tests.sql new file mode 100644 index 00000000000..b288571efe9 --- /dev/null +++ b/packages/db/migrations/0403_workflow_tests.sql @@ -0,0 +1,48 @@ +ALTER TYPE "public"."usage_log_source" ADD VALUE 'workflow-test';--> statement-breakpoint +CREATE TABLE "workflow_test" ( + "id" text PRIMARY KEY NOT NULL, + "workspace_id" text NOT NULL, + "name" text NOT NULL, + "title" text NOT NULL, + "description" text, + "body_file_id" text NOT NULL, + "cases" jsonb DEFAULT '[]' NOT NULL, + "source_hash" text NOT NULL, + "created_by_user_id" text, + "deleted_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "workflow_test_run" ( + "id" text PRIMARY KEY NOT NULL, + "test_id" text NOT NULL, + "workspace_id" text NOT NULL, + "version" text NOT NULL, + "status" text DEFAULT 'running' NOT NULL, + "passed" integer DEFAULT 0 NOT NULL, + "failed" integer DEFAULT 0 NOT NULL, + "skipped" integer DEFAULT 0 NOT NULL, + "report" jsonb, + "error" text, + "source_hash" text, + "ran_against" jsonb, + "progress" jsonb, + "triggered_by_actor" jsonb NOT NULL, + "triggered_by_user_id" text, + "started_at" timestamp DEFAULT now() NOT NULL, + "completed_at" timestamp, + CONSTRAINT "workflow_test_run_version_check" CHECK ("workflow_test_run"."version" IN ('draft', 'deployed')), + CONSTRAINT "workflow_test_run_status_check" CHECK ("workflow_test_run"."status" IN ('running', 'passed', 'failed', 'error')), + CONSTRAINT "workflow_test_run_completed_check" CHECK (("workflow_test_run"."status" = 'running') = ("workflow_test_run"."completed_at" IS NULL)) +); +--> statement-breakpoint +ALTER TABLE "workflow_test" ADD CONSTRAINT "workflow_test_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_test" ADD CONSTRAINT "workflow_test_body_file_id_workspace_files_id_fk" FOREIGN KEY ("body_file_id") REFERENCES "public"."workspace_files"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_test" ADD CONSTRAINT "workflow_test_created_by_user_id_user_id_fk" FOREIGN KEY ("created_by_user_id") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_test_run" ADD CONSTRAINT "workflow_test_run_test_id_workflow_test_id_fk" FOREIGN KEY ("test_id") REFERENCES "public"."workflow_test"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_test_run" ADD CONSTRAINT "workflow_test_run_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "workflow_test_run" ADD CONSTRAINT "workflow_test_run_triggered_by_user_id_user_id_fk" FOREIGN KEY ("triggered_by_user_id") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "workflow_test_workspace_name_unique" ON "workflow_test" USING btree ("workspace_id","name") WHERE "workflow_test"."deleted_at" IS NULL;--> statement-breakpoint +CREATE UNIQUE INDEX "workflow_test_body_file_unique" ON "workflow_test" USING btree ("body_file_id");--> statement-breakpoint +CREATE INDEX "workflow_test_run_test_version_started_idx" ON "workflow_test_run" USING btree ("test_id","version","started_at"); \ No newline at end of file diff --git a/packages/db/migrations/meta/0403_snapshot.json b/packages/db/migrations/meta/0403_snapshot.json new file mode 100644 index 00000000000..1f21fe6b447 --- /dev/null +++ b/packages/db/migrations/meta/0403_snapshot.json @@ -0,0 +1,30533 @@ +{ + "id": "44e8985f-7e13-4c8f-84d7-be85dc491ba6", + "prevId": "ebc04483-d023-4055-a111-518efb74435f", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.academy_certificate": { + "name": "academy_certificate", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "course_id": { + "name": "course_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "academy_cert_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "issued_at": { + "name": "issued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "certificate_number": { + "name": "certificate_number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "academy_certificate_user_id_idx": { + "name": "academy_certificate_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "academy_certificate_course_id_idx": { + "name": "academy_certificate_course_id_idx", + "columns": [ + { + "expression": "course_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "academy_certificate_user_course_unique": { + "name": "academy_certificate_user_course_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "course_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "academy_certificate_status_idx": { + "name": "academy_certificate_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "academy_certificate_user_id_user_id_fk": { + "name": "academy_certificate_user_id_user_id_fk", + "tableFrom": "academy_certificate", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "academy_certificate_certificate_number_unique": { + "name": "academy_certificate_certificate_number_unique", + "nullsNotDistinct": false, + "columns": ["certificate_number"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_config": { + "name": "oauth_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_user_id_idx": { + "name": "account_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_account_on_account_id_provider_id": { + "name": "idx_account_on_account_id_provider_id", + "columns": [ + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_memory_turn": { + "name": "agent_memory_turn", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "memory_id": { + "name": "memory_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "node_id": { + "name": "node_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_order": { + "name": "execution_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "encrypted_state": { + "name": "encrypted_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_memory_turn_invocation_unique": { + "name": "agent_memory_turn_invocation_unique", + "columns": [ + { + "expression": "memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "node_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_memory_turn_workflow_idx": { + "name": "agent_memory_turn_workflow_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_memory_turn_memory_id_memory_id_fk": { + "name": "agent_memory_turn_memory_id_memory_id_fk", + "tableFrom": "agent_memory_turn", + "tableTo": "memory", + "columnsFrom": ["memory_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_memory_turn_workflow_id_workflow_id_fk": { + "name": "agent_memory_turn_workflow_id_workflow_id_fk", + "tableFrom": "agent_memory_turn", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_key": { + "name": "api_key", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_hash": { + "name": "key_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'personal'" + }, + "last_used": { + "name": "last_used", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "api_key_workspace_type_idx": { + "name": "api_key_workspace_type_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_key_user_type_idx": { + "name": "api_key_user_type_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_key_key_hash_idx": { + "name": "api_key_key_hash_idx", + "columns": [ + { + "expression": "key_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_key_user_id_user_id_fk": { + "name": "api_key_user_id_user_id_fk", + "tableFrom": "api_key", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "api_key_workspace_id_workspace_id_fk": { + "name": "api_key_workspace_id_workspace_id_fk", + "tableFrom": "api_key", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "api_key_created_by_user_id_fk": { + "name": "api_key_created_by_user_id_fk", + "tableFrom": "api_key", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_key_key_unique": { + "name": "api_key_key_unique", + "nullsNotDistinct": false, + "columns": ["key"] + } + }, + "policies": {}, + "checkConstraints": { + "workspace_type_check": { + "name": "workspace_type_check", + "value": "(type = 'workspace' AND workspace_id IS NOT NULL) OR (type = 'personal' AND workspace_id IS NULL)" + } + }, + "isRLSEnabled": false + }, + "public.async_jobs": { + "name": "async_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "run_at": { + "name": "run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_attempts": { + "name": "max_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "output": { + "name": "output", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "async_jobs_status_started_at_idx": { + "name": "async_jobs_status_started_at_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "async_jobs_status_completed_at_idx": { + "name": "async_jobs_status_completed_at_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "completed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "async_jobs_schedule_pending_run_at_idx": { + "name": "async_jobs_schedule_pending_run_at_idx", + "columns": [ + { + "expression": "run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"async_jobs\".\"type\" = 'schedule-execution' AND \"async_jobs\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "async_jobs_schedule_processing_started_at_idx": { + "name": "async_jobs_schedule_processing_started_at_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"async_jobs\".\"type\" = 'schedule-execution' AND \"async_jobs\".\"status\" = 'processing'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "async_jobs_schedule_unreconciled_terminal_idx": { + "name": "async_jobs_schedule_unreconciled_terminal_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"async_jobs\".\"type\" = 'schedule-execution' AND \"async_jobs\".\"status\" IN ('completed', 'failed', 'cancelled') AND COALESCE(\"async_jobs\".\"metadata\" ->> 'scheduleReconciled', 'false') <> 'true'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.audit_log": { + "name": "audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_name": { + "name": "actor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_email": { + "name": "actor_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_name": { + "name": "resource_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "surface": { + "name": "surface", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_log_workspace_created_idx": { + "name": "audit_log_workspace_created_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_log_workspace_created_at_id_idx": { + "name": "audit_log_workspace_created_at_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_trunc('milliseconds', \"created_at\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_log_actor_created_idx": { + "name": "audit_log_actor_created_idx", + "columns": [ + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_log_resource_idx": { + "name": "audit_log_resource_idx", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_log_action_idx": { + "name": "audit_log_action_idx", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_log_workspace_id_workspace_id_fk": { + "name": "audit_log_workspace_id_workspace_id_fk", + "tableFrom": "audit_log", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "audit_log_actor_id_user_id_fk": { + "name": "audit_log_actor_id_user_id_fk", + "tableFrom": "audit_log", + "tableTo": "user", + "columnsFrom": ["actor_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.background_work_status": { + "name": "background_work_status", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "background_work_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "background_work_status_value", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "background_work_status_workspace_status_idx": { + "name": "background_work_status_workspace_status_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "background_work_status_workflow_status_idx": { + "name": "background_work_status_workflow_status_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "background_work_status_meta_child_ws_idx": { + "name": "background_work_status_meta_child_ws_idx", + "columns": [ + { + "expression": "(\"metadata\" ->> 'childWorkspaceId')", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "background_work_status_meta_other_ws_idx": { + "name": "background_work_status_meta_other_ws_idx", + "columns": [ + { + "expression": "(\"metadata\" ->> 'otherWorkspaceId')", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "background_work_status_workspace_id_workspace_id_fk": { + "name": "background_work_status_workspace_id_workspace_id_fk", + "tableFrom": "background_work_status", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "background_work_status_workflow_id_workflow_id_fk": { + "name": "background_work_status_workflow_id_workflow_id_fk", + "tableFrom": "background_work_status", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat": { + "name": "chat", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "customizations": { + "name": "customizations", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'public'" + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_emails": { + "name": "allowed_emails", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "output_configs": { + "name": "output_configs", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "include_thinking": { + "name": "include_thinking", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "include_tool_calls": { + "name": "include_tool_calls", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "identifier_idx": { + "name": "identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_archived_at_partial_idx": { + "name": "chat_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"chat\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_chat_on_workflow_id_archived_at": { + "name": "idx_chat_on_workflow_id_archived_at", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_workflow_id_workflow_id_fk": { + "name": "chat_workflow_id_workflow_id_fk", + "tableFrom": "chat", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_user_id_user_id_fk": { + "name": "chat_user_id_user_id_fk", + "tableFrom": "chat", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.client_credential_token": { + "name": "client_credential_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "encrypted_value": { + "name": "encrypted_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token_digest": { + "name": "access_token_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "client_credential_token_expires_at_idx": { + "name": "client_credential_token_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"client_credential_token\".\"access_token_digest\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "client_credential_token_access_token_digest_idx": { + "name": "client_credential_token_access_token_digest_idx", + "columns": [ + { + "expression": "access_token_digest", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"client_credential_token\".\"access_token_digest\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_async_tool_calls": { + "name": "copilot_async_tool_calls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "checkpoint_id": { + "name": "checkpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "tool_call_id": { + "name": "tool_call_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "args": { + "name": "args", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "status": { + "name": "status", + "type": "copilot_async_tool_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permission_decision": { + "name": "permission_decision", + "type": "copilot_tool_permission_decision", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "permission_decided_at": { + "name": "permission_decided_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "permission_requested_at": { + "name": "permission_requested_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "pickup_deadline_at": { + "name": "pickup_deadline_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "persist_seq": { + "name": "persist_seq", + "type": "bigint", + "primaryKey": false, + "notNull": false, + "default": "nextval('copilot_async_tool_calls_persist_seq')" + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "claimed_by": { + "name": "claimed_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "browser_download_started_at": { + "name": "browser_download_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "execution_started_at": { + "name": "execution_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "execution_settled_at": { + "name": "execution_settled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "execution_owner_token": { + "name": "execution_owner_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_lease_expires_at": { + "name": "execution_lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "execution_revoked_at": { + "name": "execution_revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "client_workflow_execution_id": { + "name": "client_workflow_execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sandbox_processes": { + "name": "sandbox_processes", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_async_tool_calls_run_id_idx": { + "name": "copilot_async_tool_calls_run_id_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_async_tool_calls_checkpoint_id_idx": { + "name": "copilot_async_tool_calls_checkpoint_id_idx", + "columns": [ + { + "expression": "checkpoint_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_async_tool_calls_status_idx": { + "name": "copilot_async_tool_calls_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_async_tool_calls_run_status_idx": { + "name": "copilot_async_tool_calls_run_status_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_async_tool_calls_tool_call_id_unique": { + "name": "copilot_async_tool_calls_tool_call_id_unique", + "columns": [ + { + "expression": "tool_call_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_async_tool_calls_run_id_copilot_runs_id_fk": { + "name": "copilot_async_tool_calls_run_id_copilot_runs_id_fk", + "tableFrom": "copilot_async_tool_calls", + "tableTo": "copilot_runs", + "columnsFrom": ["run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_async_tool_calls_checkpoint_id_copilot_run_checkpoints_id_fk": { + "name": "copilot_async_tool_calls_checkpoint_id_copilot_run_checkpoints_id_fk", + "tableFrom": "copilot_async_tool_calls", + "tableTo": "copilot_run_checkpoints", + "columnsFrom": ["checkpoint_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_chats": { + "name": "copilot_chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "chat_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'copilot'" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'claude-3-7-sonnet-latest'" + }, + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_conversation_key": { + "name": "external_conversation_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_conversation_metadata": { + "name": "external_conversation_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "preview_yaml": { + "name": "preview_yaml", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan_artifact": { + "name": "plan_artifact", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "resources": { + "name": "resources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "auto_allowed_tools": { + "name": "auto_allowed_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "pinned": { + "name": "pinned", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_chats_organization_id_idx": { + "name": "copilot_chats_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_external_conversation_unique": { + "name": "copilot_chats_external_conversation_unique", + "columns": [ + { + "expression": "external_conversation_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"copilot_chats\".\"external_conversation_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_org_created_idx": { + "name": "copilot_chats_user_org_created_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_id_idx": { + "name": "copilot_chats_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_workflow_id_idx": { + "name": "copilot_chats_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_workflow_idx": { + "name": "copilot_chats_user_workflow_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_workspace_idx": { + "name": "copilot_chats_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_created_at_idx": { + "name": "copilot_chats_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_updated_at_idx": { + "name": "copilot_chats_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_workspace_created_at_id_idx": { + "name": "copilot_chats_workspace_created_at_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_trunc('milliseconds', \"created_at\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_workspace_deleted_partial_idx": { + "name": "copilot_chats_user_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_chats\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_chats_user_id_user_id_fk": { + "name": "copilot_chats_user_id_user_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_chats_workflow_id_workflow_id_fk": { + "name": "copilot_chats_workflow_id_workflow_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_chats_workspace_id_workspace_id_fk": { + "name": "copilot_chats_workspace_id_workspace_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_chats_organization_id_organization_id_fk": { + "name": "copilot_chats_organization_id_organization_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "copilot_chats_owner_check": { + "name": "copilot_chats_owner_check", + "value": "num_nonnulls(\"copilot_chats\".\"workspace_id\", \"copilot_chats\".\"organization_id\") <= 1" + }, + "copilot_chats_organization_workflow_check": { + "name": "copilot_chats_organization_workflow_check", + "value": "\"copilot_chats\".\"organization_id\" IS NULL OR \"copilot_chats\".\"workflow_id\" IS NULL" + } + }, + "isRLSEnabled": false + }, + "public.copilot_feedback": { + "name": "copilot_feedback", + "schema": "", + "columns": { + "feedback_id": { + "name": "feedback_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_query": { + "name": "user_query", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_response": { + "name": "agent_response", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_positive": { + "name": "is_positive", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_yaml": { + "name": "workflow_yaml", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_feedback_user_id_idx": { + "name": "copilot_feedback_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_chat_id_idx": { + "name": "copilot_feedback_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_user_chat_idx": { + "name": "copilot_feedback_user_chat_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_is_positive_idx": { + "name": "copilot_feedback_is_positive_idx", + "columns": [ + { + "expression": "is_positive", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_created_at_idx": { + "name": "copilot_feedback_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_feedback_user_id_user_id_fk": { + "name": "copilot_feedback_user_id_user_id_fk", + "tableFrom": "copilot_feedback", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_feedback_chat_id_copilot_chats_id_fk": { + "name": "copilot_feedback_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_feedback", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_messages": { + "name": "copilot_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "stream_id": { + "name": "stream_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_message_id": { + "name": "parent_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tokens_in": { + "name": "tokens_in", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tokens_out": { + "name": "tokens_out", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "seq": { + "name": "seq", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_messages_chat_message_unique": { + "name": "copilot_messages_chat_message_unique", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_messages_chat_created_at_idx": { + "name": "copilot_messages_chat_created_at_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_messages\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_messages_chat_seq_idx": { + "name": "copilot_messages_chat_seq_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "seq", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_messages\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_messages_chat_stream_idx": { + "name": "copilot_messages_chat_stream_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stream_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_messages\".\"stream_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_messages_user_created_at_idx": { + "name": "copilot_messages_user_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_messages\".\"role\" = 'user' AND \"copilot_messages\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_messages_chat_id_copilot_chats_id_fk": { + "name": "copilot_messages_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_messages", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_organization_request_stops": { + "name": "copilot_organization_request_stops", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stream_id": { + "name": "stream_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stopped_at": { + "name": "stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "copilot_organization_request_stops_user_id_user_id_fk": { + "name": "copilot_organization_request_stops_user_id_user_id_fk", + "tableFrom": "copilot_organization_request_stops", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_organization_request_stops_organization_id_organization_id_fk": { + "name": "copilot_organization_request_stops_organization_id_organization_id_fk", + "tableFrom": "copilot_organization_request_stops", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "copilot_organization_request_stops_user_id_organization_id_stream_id_pk": { + "name": "copilot_organization_request_stops_user_id_organization_id_stream_id_pk", + "columns": ["user_id", "organization_id", "stream_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_request_stops": { + "name": "copilot_request_stops", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stream_id": { + "name": "stream_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stopped_at": { + "name": "stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "copilot_request_stops_user_id_user_id_fk": { + "name": "copilot_request_stops_user_id_user_id_fk", + "tableFrom": "copilot_request_stops", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_request_stops_workspace_id_workspace_id_fk": { + "name": "copilot_request_stops_workspace_id_workspace_id_fk", + "tableFrom": "copilot_request_stops", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "copilot_request_stops_user_id_workspace_id_stream_id_pk": { + "name": "copilot_request_stops_user_id_workspace_id_stream_id_pk", + "columns": ["user_id", "workspace_id", "stream_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_run_checkpoints": { + "name": "copilot_run_checkpoints", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pending_tool_call_id": { + "name": "pending_tool_call_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conversation_snapshot": { + "name": "conversation_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "agent_state": { + "name": "agent_state", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "provider_request": { + "name": "provider_request", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_run_checkpoints_run_id_idx": { + "name": "copilot_run_checkpoints_run_id_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_run_checkpoints_pending_tool_call_id_idx": { + "name": "copilot_run_checkpoints_pending_tool_call_id_idx", + "columns": [ + { + "expression": "pending_tool_call_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_run_checkpoints_run_pending_tool_unique": { + "name": "copilot_run_checkpoints_run_pending_tool_unique", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pending_tool_call_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_run_checkpoints_run_id_copilot_runs_id_fk": { + "name": "copilot_run_checkpoints_run_id_copilot_runs_id_fk", + "tableFrom": "copilot_run_checkpoints", + "tableTo": "copilot_runs", + "columnsFrom": ["run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_runs": { + "name": "copilot_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_execution_version": { + "name": "tool_execution_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tool_admission_closed_at": { + "name": "tool_admission_closed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "parent_run_id": { + "name": "parent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stream_id": { + "name": "stream_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent": { + "name": "agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "copilot_run_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "request_context": { + "name": "request_context", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "desktop_device_id": { + "name": "desktop_device_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "copilot_runs_parent_run_id_idx": { + "name": "copilot_runs_parent_run_id_idx", + "columns": [ + { + "expression": "parent_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_chat_id_idx": { + "name": "copilot_runs_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_chat_started_at_idx": { + "name": "copilot_runs_chat_started_at_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_user_id_idx": { + "name": "copilot_runs_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_workflow_id_idx": { + "name": "copilot_runs_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_workspace_id_idx": { + "name": "copilot_runs_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_status_idx": { + "name": "copilot_runs_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_chat_execution_idx": { + "name": "copilot_runs_chat_execution_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_execution_started_at_idx": { + "name": "copilot_runs_execution_started_at_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_workspace_completed_at_id_idx": { + "name": "copilot_runs_workspace_completed_at_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_trunc('milliseconds', \"completed_at\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_stream_id_unique": { + "name": "copilot_runs_stream_id_unique", + "columns": [ + { + "expression": "stream_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_runs_desktop_device_started_at_idx": { + "name": "copilot_runs_desktop_device_started_at_idx", + "columns": [ + { + "expression": "desktop_device_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"copilot_runs\".\"desktop_device_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_runs_chat_id_copilot_chats_id_fk": { + "name": "copilot_runs_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_runs_user_id_user_id_fk": { + "name": "copilot_runs_user_id_user_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_runs_workflow_id_workflow_id_fk": { + "name": "copilot_runs_workflow_id_workflow_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_runs_workspace_id_workspace_id_fk": { + "name": "copilot_runs_workspace_id_workspace_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_runs_organization_id_organization_id_fk": { + "name": "copilot_runs_organization_id_organization_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_runs_desktop_device_id_desktop_devices_id_fk": { + "name": "copilot_runs_desktop_device_id_desktop_devices_id_fk", + "tableFrom": "copilot_runs", + "tableTo": "desktop_devices", + "columnsFrom": ["desktop_device_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_service_usage": { + "name": "copilot_service_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "stream_id": { + "name": "stream_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tool_call_id": { + "name": "tool_call_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "service": { + "name": "service", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cost_usd": { + "name": "cost_usd", + "type": "numeric(12, 8)", + "primaryKey": false, + "notNull": false + }, + "worker_origin": { + "name": "worker_origin", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "copilot_service_usage_pending_idx": { + "name": "copilot_service_usage_pending_idx", + "columns": [ + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "delivered_at IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_task_subscriptions": { + "name": "copilot_task_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_task_subscriptions_execution_idx": { + "name": "copilot_task_subscriptions_execution_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_task_subscriptions_task_idx": { + "name": "copilot_task_subscriptions_task_idx", + "columns": [ + { + "expression": "task_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_task_subscriptions_chat_id_copilot_chats_id_fk": { + "name": "copilot_task_subscriptions_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_task_subscriptions", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_task_subscriptions_workspace_id_workspace_id_fk": { + "name": "copilot_task_subscriptions_workspace_id_workspace_id_fk", + "tableFrom": "copilot_task_subscriptions", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_task_subscriptions_user_id_user_id_fk": { + "name": "copilot_task_subscriptions_user_id_user_id_fk", + "tableFrom": "copilot_task_subscriptions", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_workflow_read_hashes": { + "name": "copilot_workflow_read_hashes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_workflow_read_hashes_chat_id_idx": { + "name": "copilot_workflow_read_hashes_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_workflow_read_hashes_workflow_id_idx": { + "name": "copilot_workflow_read_hashes_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_workflow_read_hashes_chat_workflow_unique": { + "name": "copilot_workflow_read_hashes_chat_workflow_unique", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_workflow_read_hashes_chat_id_copilot_chats_id_fk": { + "name": "copilot_workflow_read_hashes_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_workflow_read_hashes", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_workflow_read_hashes_workflow_id_workflow_id_fk": { + "name": "copilot_workflow_read_hashes_workflow_id_workflow_id_fk", + "tableFrom": "copilot_workflow_read_hashes", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.credential": { + "name": "credential", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slack_app_id": { + "name": "slack_app_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "credential_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "unredacted": { + "name": "unredacted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env_key": { + "name": "env_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env_owner_user_id": { + "name": "env_owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_service_account_key": { + "name": "encrypted_service_account_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_personal_token": { + "name": "encrypted_personal_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorization_app_id": { + "name": "authorization_app_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_group_enrollment_id": { + "name": "credential_group_enrollment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_group_option_id": { + "name": "credential_group_option_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mcp_server_id": { + "name": "mcp_server_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mcp_oauth_config_version": { + "name": "mcp_oauth_config_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "managed_oauth_scope_version": { + "name": "managed_oauth_scope_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "provider_subject_id": { + "name": "provider_subject_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_tenant_id": { + "name": "provider_tenant_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "managed_oauth_status": { + "name": "managed_oauth_status", + "type": "managed_oauth_credential_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "granted_scopes": { + "name": "granted_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "provider_metadata": { + "name": "provider_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "encrypted_oauth_token_set": { + "name": "encrypted_oauth_token_set", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mcp_tools": { + "name": "mcp_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "mcp_tools_refreshed_at": { + "name": "mcp_tools_refreshed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "granted_at": { + "name": "granted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_refreshed_at": { + "name": "last_refreshed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "credential_organization_id_idx": { + "name": "credential_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_organization_account_unique": { + "name": "credential_organization_account_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credential\".\"account_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_org_personal_token_unique": { + "name": "credential_org_personal_token_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subject_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credential\".\"type\" = 'personal_token'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_workspace_id_idx": { + "name": "credential_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_type_idx": { + "name": "credential_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_provider_id_idx": { + "name": "credential_provider_id_idx", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_account_id_idx": { + "name": "credential_account_id_idx", + "columns": [ + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_env_owner_user_id_idx": { + "name": "credential_env_owner_user_id_idx", + "columns": [ + { + "expression": "env_owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_idx": { + "name": "credential_group_enrollment_idx", + "columns": [ + { + "expression": "credential_group_enrollment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_mcp_server_idx": { + "name": "credential_mcp_server_idx", + "columns": [ + { + "expression": "mcp_server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_option_unique": { + "name": "credential_group_option_unique", + "columns": [ + { + "expression": "credential_group_enrollment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "credential_group_option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credential\".\"type\" = 'managed_oauth'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_managed_mcp_enrollment_server_unique": { + "name": "credential_managed_mcp_enrollment_server_unique", + "columns": [ + { + "expression": "credential_group_enrollment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "mcp_server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credential\".\"type\" = 'managed_mcp'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_workspace_account_unique": { + "name": "credential_workspace_account_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "account_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_workspace_env_unique": { + "name": "credential_workspace_env_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "env_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "type = 'env_workspace'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_workspace_personal_env_unique": { + "name": "credential_workspace_personal_env_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "env_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "env_owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "type = 'env_personal'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_personal_token_identity_unique": { + "name": "credential_personal_token_identity_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subject_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "type = 'personal_token'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "credential_workspace_id_workspace_id_fk": { + "name": "credential_workspace_id_workspace_id_fk", + "tableFrom": "credential", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_organization_id_organization_id_fk": { + "name": "credential_organization_id_organization_id_fk", + "tableFrom": "credential", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_slack_app_id_slack_app_id_fk": { + "name": "credential_slack_app_id_slack_app_id_fk", + "tableFrom": "credential", + "tableTo": "slack_app", + "columnsFrom": ["slack_app_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "credential_account_id_account_id_fk": { + "name": "credential_account_id_account_id_fk", + "tableFrom": "credential", + "tableTo": "account", + "columnsFrom": ["account_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_env_owner_user_id_user_id_fk": { + "name": "credential_env_owner_user_id_user_id_fk", + "tableFrom": "credential", + "tableTo": "user", + "columnsFrom": ["env_owner_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_credential_group_enrollment_id_credential_group_enrollment_id_fk": { + "name": "credential_credential_group_enrollment_id_credential_group_enrollment_id_fk", + "tableFrom": "credential", + "tableTo": "credential_group_enrollment", + "columnsFrom": ["credential_group_enrollment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_mcp_server_id_mcp_servers_id_fk": { + "name": "credential_mcp_server_id_mcp_servers_id_fk", + "tableFrom": "credential", + "tableTo": "mcp_servers", + "columnsFrom": ["mcp_server_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_created_by_user_id_fk": { + "name": "credential_created_by_user_id_fk", + "tableFrom": "credential", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "credential_owner_check": { + "name": "credential_owner_check", + "value": "num_nonnulls(\"credential\".\"workspace_id\", \"credential\".\"organization_id\") = 1" + }, + "credential_organization_type_check": { + "name": "credential_organization_type_check", + "value": "\"credential\".\"organization_id\" IS NULL OR \"credential\".\"type\" IN ('oauth', 'managed_oauth', 'managed_mcp', 'service_account', 'personal_token')" + }, + "credential_personal_token_source_check": { + "name": "credential_personal_token_source_check", + "value": "(type::text <> 'personal_token') OR (\n created_by IS NOT NULL\n AND provider_id IS NOT NULL\n AND provider_id = 'gitlab'\n AND provider_subject_id IS NOT NULL\n AND provider_tenant_id IS NOT NULL\n AND encrypted_personal_token IS NOT NULL\n AND granted_scopes IS NOT NULL\n AND cardinality(granted_scopes) > 0\n AND account_id IS NULL\n AND env_key IS NULL\n AND env_owner_user_id IS NULL\n AND authorization_app_id IS NULL\n AND encrypted_oauth_token_set IS NULL\n AND encrypted_service_account_key IS NULL\n AND unredacted = false\n )" + }, + "credential_oauth_source_check": { + "name": "credential_oauth_source_check", + "value": "(type <> 'oauth') OR (account_id IS NOT NULL AND provider_id IS NOT NULL)" + }, + "credential_managed_oauth_source_check": { + "name": "credential_managed_oauth_source_check", + "value": "(type::text <> 'managed_oauth') OR (\n account_id IS NULL\n AND provider_id IS NOT NULL\n AND authorization_app_id IS NOT NULL\n AND provider_subject_id IS NOT NULL\n AND managed_oauth_status IS NOT NULL\n AND granted_scopes IS NOT NULL\n AND encrypted_oauth_token_set IS NOT NULL\n AND granted_at IS NOT NULL\n )" + }, + "credential_managed_oauth_group_binding_check": { + "name": "credential_managed_oauth_group_binding_check", + "value": "(type::text <> 'managed_oauth') OR (\n credential_group_enrollment_id IS NOT NULL\n AND credential_group_option_id IS NOT NULL\n AND managed_oauth_scope_version IS NOT NULL\n AND managed_oauth_scope_version > 0\n )" + }, + "credential_managed_mcp_source_check": { + "name": "credential_managed_mcp_source_check", + "value": "(type::text <> 'managed_mcp') OR (\n id LIKE 'mcp-cg-%'\n AND account_id IS NULL\n AND provider_id IS NULL\n AND authorization_app_id IS NULL\n AND credential_group_enrollment_id IS NOT NULL\n AND credential_group_option_id IS NULL\n AND mcp_server_id IS NOT NULL\n AND managed_oauth_status IS NOT NULL\n AND (managed_oauth_status <> 'active' OR (\n encrypted_oauth_token_set IS NOT NULL\n AND mcp_tools IS NOT NULL\n ))\n AND granted_at IS NOT NULL\n AND managed_oauth_scope_version IS NULL\n AND provider_subject_id IS NULL\n AND provider_tenant_id IS NULL\n AND granted_scopes IS NULL\n AND provider_metadata IS NULL\n AND created_by IS NULL\n AND env_key IS NULL\n AND env_owner_user_id IS NULL\n AND encrypted_service_account_key IS NULL\n AND unredacted = false\n )" + }, + "credential_creator_source_check": { + "name": "credential_creator_source_check", + "value": "(type::text = 'managed_mcp') OR created_by IS NOT NULL" + }, + "credential_workspace_env_source_check": { + "name": "credential_workspace_env_source_check", + "value": "(type <> 'env_workspace') OR (env_key IS NOT NULL AND env_owner_user_id IS NULL)" + }, + "credential_personal_env_source_check": { + "name": "credential_personal_env_source_check", + "value": "(type <> 'env_personal') OR (env_key IS NOT NULL AND env_owner_user_id IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.credential_group": { + "name": "credential_group", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "options": { + "name": "options", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "encrypted_provider_configuration": { + "name": "encrypted_provider_configuration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "credential_group_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "credential_group_organization_id_idx": { + "name": "credential_group_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_organization_unique": { + "name": "credential_group_organization_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_public_id_unique": { + "name": "credential_group_public_id_unique", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_workspace_unique": { + "name": "credential_group_workspace_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "credential_group_workspace_id_workspace_id_fk": { + "name": "credential_group_workspace_id_workspace_id_fk", + "tableFrom": "credential_group", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_group_organization_id_organization_id_fk": { + "name": "credential_group_organization_id_organization_id_fk", + "tableFrom": "credential_group", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_group_created_by_user_id_fk": { + "name": "credential_group_created_by_user_id_fk", + "tableFrom": "credential_group", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "credential_group_owner_check": { + "name": "credential_group_owner_check", + "value": "num_nonnulls(\"credential_group\".\"workspace_id\", \"credential_group\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.credential_group_enrollment": { + "name": "credential_group_enrollment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "credential_group_id": { + "name": "credential_group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "credential_group_enrollment_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'invited'" + }, + "invitation_token_hash": { + "name": "invitation_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invitation_expires_at": { + "name": "invitation_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "invited_at": { + "name": "invited_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_delivery_error": { + "name": "last_delivery_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "credential_group_enrollment_group_user_unique": { + "name": "credential_group_enrollment_group_user_unique", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credential_group_enrollment\".\"user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_user_id_idx": { + "name": "credential_group_enrollment_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_group_email_unique": { + "name": "credential_group_enrollment_group_email_unique", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_invitation_token_hash_unique": { + "name": "credential_group_enrollment_invitation_token_hash_unique", + "columns": [ + { + "expression": "invitation_token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_group_status_idx": { + "name": "credential_group_enrollment_group_status_idx", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_group_enrollment_group_invited_at_id_idx": { + "name": "credential_group_enrollment_group_invited_at_id_idx", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "invited_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "credential_group_enrollment_credential_group_id_credential_group_id_fk": { + "name": "credential_group_enrollment_credential_group_id_credential_group_id_fk", + "tableFrom": "credential_group_enrollment", + "tableTo": "credential_group", + "columnsFrom": ["credential_group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_group_enrollment_user_id_user_id_fk": { + "name": "credential_group_enrollment_user_id_user_id_fk", + "tableFrom": "credential_group_enrollment", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_group_enrollment_created_by_user_id_fk": { + "name": "credential_group_enrollment_created_by_user_id_fk", + "tableFrom": "credential_group_enrollment", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "credential_group_enrollment_normalized_email_check": { + "name": "credential_group_enrollment_normalized_email_check", + "value": "\"credential_group_enrollment\".\"email\" = lower(btrim(\"credential_group_enrollment\".\"email\")) AND length(\"credential_group_enrollment\".\"email\") BETWEEN 3 AND 320" + }, + "credential_group_enrollment_invitation_token_hash_length_check": { + "name": "credential_group_enrollment_invitation_token_hash_length_check", + "value": "length(\"credential_group_enrollment\".\"invitation_token_hash\") = 64" + } + }, + "isRLSEnabled": false + }, + "public.credential_member": { + "name": "credential_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "credential_member_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "status": { + "name": "status", + "type": "credential_member_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "invited_by": { + "name": "invited_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "credential_member_user_id_idx": { + "name": "credential_member_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_member_role_idx": { + "name": "credential_member_role_idx", + "columns": [ + { + "expression": "role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_member_status_idx": { + "name": "credential_member_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "credential_member_unique": { + "name": "credential_member_unique", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "credential_member_credential_id_credential_id_fk": { + "name": "credential_member_credential_id_credential_id_fk", + "tableFrom": "credential_member", + "tableTo": "credential", + "columnsFrom": ["credential_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_member_user_id_user_id_fk": { + "name": "credential_member_user_id_user_id_fk", + "tableFrom": "credential_member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "credential_member_invited_by_user_id_fk": { + "name": "credential_member_invited_by_user_id_fk", + "tableFrom": "credential_member", + "tableTo": "user", + "columnsFrom": ["invited_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_block": { + "name": "custom_block", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inputs": { + "name": "inputs", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "outputs": { + "name": "outputs", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "trace_child_runs": { + "name": "trace_child_runs", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "custom_block_organization_id_idx": { + "name": "custom_block_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "custom_block_workflow_id_idx": { + "name": "custom_block_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "custom_block_organization_type_unique": { + "name": "custom_block_organization_type_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "custom_block_organization_id_organization_id_fk": { + "name": "custom_block_organization_id_organization_id_fk", + "tableFrom": "custom_block", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "custom_block_workflow_id_workflow_id_fk": { + "name": "custom_block_workflow_id_workflow_id_fk", + "tableFrom": "custom_block", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "custom_block_created_by_user_id_fk": { + "name": "custom_block_created_by_user_id_fk", + "tableFrom": "custom_block", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_tools": { + "name": "custom_tools", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema": { + "name": "schema", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "custom_tools_workspace_id_idx": { + "name": "custom_tools_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "custom_tools_workspace_title_unique": { + "name": "custom_tools_workspace_title_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "title", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "custom_tools_workspace_id_workspace_id_fk": { + "name": "custom_tools_workspace_id_workspace_id_fk", + "tableFrom": "custom_tools", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "custom_tools_user_id_user_id_fk": { + "name": "custom_tools_user_id_user_id_fk", + "tableFrom": "custom_tools", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dashboard": { + "name": "dashboard", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "dashboard_workspace_id_unique": { + "name": "dashboard_workspace_id_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dashboard_workspace_id_workspace_id_fk": { + "name": "dashboard_workspace_id_workspace_id_fk", + "tableFrom": "dashboard", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "dashboard_created_by_user_id_fk": { + "name": "dashboard_created_by_user_id_fk", + "tableFrom": "dashboard", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "dashboard_updated_by_user_id_fk": { + "name": "dashboard_updated_by_user_id_fk", + "tableFrom": "dashboard", + "tableTo": "user", + "columnsFrom": ["updated_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.data_drain_runs": { + "name": "data_drain_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "drain_id": { + "name": "drain_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "data_drain_run_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "data_drain_run_trigger", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "rows_exported": { + "name": "rows_exported", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "bytes_written": { + "name": "bytes_written", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cursor_before": { + "name": "cursor_before", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cursor_after": { + "name": "cursor_after", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "locators": { + "name": "locators", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + } + }, + "indexes": { + "data_drain_runs_drain_started_idx": { + "name": "data_drain_runs_drain_started_idx", + "columns": [ + { + "expression": "drain_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "data_drain_runs_drain_id_data_drains_id_fk": { + "name": "data_drain_runs_drain_id_data_drains_id_fk", + "tableFrom": "data_drain_runs", + "tableTo": "data_drains", + "columnsFrom": ["drain_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.data_drains": { + "name": "data_drains", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "data_drain_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "destination_type": { + "name": "destination_type", + "type": "data_drain_destination", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "destination_config": { + "name": "destination_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "destination_credentials": { + "name": "destination_credentials", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schedule_cadence": { + "name": "schedule_cadence", + "type": "data_drain_cadence", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "cursor": { + "name": "cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_success_at": { + "name": "last_success_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "data_drains_org_idx": { + "name": "data_drains_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_drains_due_idx": { + "name": "data_drains_due_idx", + "columns": [ + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_drains_org_name_unique": { + "name": "data_drains_org_name_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "data_drains_organization_id_organization_id_fk": { + "name": "data_drains_organization_id_organization_id_fk", + "tableFrom": "data_drains", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "data_drains_created_by_user_id_fk": { + "name": "data_drains_created_by_user_id_fk", + "tableFrom": "data_drains", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.desktop_devices": { + "name": "desktop_devices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "app_version": { + "name": "app_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "capabilities": { + "name": "capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "desktop_devices_user_id_idx": { + "name": "desktop_devices_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "desktop_devices_session_id_idx": { + "name": "desktop_devices_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "desktop_devices_user_id_user_id_fk": { + "name": "desktop_devices_user_id_user_id_fk", + "tableFrom": "desktop_devices", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "desktop_devices_session_id_session_id_fk": { + "name": "desktop_devices_session_id_session_id_fk", + "tableFrom": "desktop_devices", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.docs_embeddings": { + "name": "docs_embeddings", + "schema": "", + "columns": { + "chunk_id": { + "name": "chunk_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chunk_text": { + "name": "chunk_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_document": { + "name": "source_document", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_link": { + "name": "source_link", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header_text": { + "name": "header_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header_level": { + "name": "header_level", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": true + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "chunk_text_tsv": { + "name": "chunk_text_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "to_tsvector('english', \"docs_embeddings\".\"chunk_text\")", + "type": "stored" + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "docs_emb_source_document_idx": { + "name": "docs_emb_source_document_idx", + "columns": [ + { + "expression": "source_document", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_header_level_idx": { + "name": "docs_emb_header_level_idx", + "columns": [ + { + "expression": "header_level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_source_header_idx": { + "name": "docs_emb_source_header_idx", + "columns": [ + { + "expression": "source_document", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "header_level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_model_idx": { + "name": "docs_emb_model_idx", + "columns": [ + { + "expression": "embedding_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_created_at_idx": { + "name": "docs_emb_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_embedding_vector_hnsw_idx": { + "name": "docs_embedding_vector_hnsw_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "docs_emb_metadata_gin_idx": { + "name": "docs_emb_metadata_gin_idx", + "columns": [ + { + "expression": "metadata", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "docs_emb_chunk_text_fts_idx": { + "name": "docs_emb_chunk_text_fts_idx", + "columns": [ + { + "expression": "chunk_text_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "docs_embedding_not_null_check": { + "name": "docs_embedding_not_null_check", + "value": "\"embedding\" IS NOT NULL" + }, + "docs_header_level_check": { + "name": "docs_header_level_check", + "value": "\"header_level\" >= 1 AND \"header_level\" <= 6" + } + }, + "isRLSEnabled": false + }, + "public.document": { + "name": "document", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_url": { + "name": "file_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunk_count": { + "name": "chunk_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "character_count": { + "name": "character_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "processing_status": { + "name": "processing_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_attempts": { + "name": "processing_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "processing_queued_at": { + "name": "processing_queued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_queue_token": { + "name": "processing_queue_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_deferred_until": { + "name": "processing_deferred_until", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_completed_at": { + "name": "processing_completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_error": { + "name": "processing_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "processing_recovery_after": { + "name": "processing_recovery_after", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "user_excluded": { + "name": "user_excluded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "tag1": { + "name": "tag1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag2": { + "name": "tag2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag3": { + "name": "tag3", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag4": { + "name": "tag4", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag5": { + "name": "tag5", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag6": { + "name": "tag6", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag7": { + "name": "tag7", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "number1": { + "name": "number1", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number2": { + "name": "number2", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number3": { + "name": "number3", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number4": { + "name": "number4", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number5": { + "name": "number5", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "date1": { + "name": "date1", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "date2": { + "name": "date2", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "boolean1": { + "name": "boolean1", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "boolean2": { + "name": "boolean2", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "boolean3": { + "name": "boolean3", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_url": { + "name": "source_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_provenance_version": { + "name": "secret_provenance_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acl": { + "name": "acl", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{ws}'::text[]" + }, + "acl_requirements": { + "name": "acl_requirements", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "acl_verified_at": { + "name": "acl_verified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "source_modified_at": { + "name": "source_modified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "source_seen_at": { + "name": "source_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "doc_kb_id_idx": { + "name": "doc_kb_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_source_modified_idx": { + "name": "doc_kb_source_modified_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_modified_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"deleted_at\" IS NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "doc_acl_gin_idx": { + "name": "doc_acl_gin_idx", + "columns": [ + { + "expression": "acl", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "array_ops" + } + ], + "isUnique": false, + "where": "\"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "gin", + "with": {} + }, + "doc_filename_idx": { + "name": "doc_filename_idx", + "columns": [ + { + "expression": "filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_processing_status_idx": { + "name": "doc_processing_status_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "processing_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_processing_recovery_idx": { + "name": "doc_processing_recovery_idx", + "columns": [ + { + "expression": "uploaded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"processing_status\" IN ('pending', 'processing', 'failed') AND \"document\".\"connector_id\" IS NOT NULL AND \"document\".\"content_hash\" IS NOT NULL AND \"document\".\"storage_key\" IS NOT NULL AND \"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_processing_recovery_idx": { + "name": "doc_connector_processing_recovery_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "uploaded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"processing_status\" IN ('pending', 'processing', 'failed') AND \"document\".\"connector_id\" IS NOT NULL AND \"document\".\"content_hash\" IS NOT NULL AND \"document\".\"storage_key\" IS NOT NULL AND \"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "doc_connector_processing_status_idx": { + "name": "doc_connector_processing_status_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "processing_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"processing_status\" IN ('pending', 'processing', 'failed') AND \"document\".\"connector_id\" IS NOT NULL AND \"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_external_id_idx": { + "name": "doc_connector_external_id_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_source_lookup_idx": { + "name": "doc_connector_source_lookup_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_reconciliation_idx": { + "name": "doc_connector_reconciliation_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "COALESCE(\"source_seen_at\", '-infinity'::timestamp)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_reconciliation_v2_idx": { + "name": "doc_connector_reconciliation_v2_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "doc_active_kb_token_count_idx": { + "name": "doc_active_kb_token_count_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "token_count", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_storage_key_idx": { + "name": "doc_storage_key_idx", + "columns": [ + { + "expression": "storage_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"storage_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_archived_at_partial_idx": { + "name": "doc_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_deleted_at_partial_idx": { + "name": "doc_deleted_at_partial_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_tombstone_idx": { + "name": "doc_connector_tombstone_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"archived_at\" IS NULL AND (\"document\".\"deleted_at\" IS NOT NULL OR \"document\".\"content_hash\" IS NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_connector_live_idx": { + "name": "doc_connector_live_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag1_lower_idx": { + "name": "doc_kb_tag1_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag1\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag2_lower_idx": { + "name": "doc_kb_tag2_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag2\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag3_lower_idx": { + "name": "doc_kb_tag3_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag3\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag4_lower_idx": { + "name": "doc_kb_tag4_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag4\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag5_lower_idx": { + "name": "doc_kb_tag5_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag5\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag6_lower_idx": { + "name": "doc_kb_tag6_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag6\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_tag7_lower_idx": { + "name": "doc_kb_tag7_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag7\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_number1_idx": { + "name": "doc_number1_idx", + "columns": [ + { + "expression": "number1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_number2_idx": { + "name": "doc_number2_idx", + "columns": [ + { + "expression": "number2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_number3_idx": { + "name": "doc_number3_idx", + "columns": [ + { + "expression": "number3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_number4_idx": { + "name": "doc_number4_idx", + "columns": [ + { + "expression": "number4", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_number5_idx": { + "name": "doc_number5_idx", + "columns": [ + { + "expression": "number5", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_date1_idx": { + "name": "doc_date1_idx", + "columns": [ + { + "expression": "date1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "doc_date2_idx": { + "name": "doc_date2_idx", + "columns": [ + { + "expression": "date2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "doc_boolean1_idx": { + "name": "doc_boolean1_idx", + "columns": [ + { + "expression": "boolean1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_boolean2_idx": { + "name": "doc_boolean2_idx", + "columns": [ + { + "expression": "boolean2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_boolean3_idx": { + "name": "doc_boolean3_idx", + "columns": [ + { + "expression": "boolean3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_knowledge_base_id_knowledge_base_id_fk": { + "name": "document_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "document", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "document_connector_id_knowledge_connector_id_fk": { + "name": "document_connector_id_knowledge_connector_id_fk", + "tableFrom": "document", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "document_uploaded_by_user_id_fk": { + "name": "document_uploaded_by_user_id_fk", + "tableFrom": "document", + "tableTo": "user", + "columnsFrom": ["uploaded_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "doc_acl_token_shape_check": { + "name": "doc_acl_token_shape_check", + "value": "array_position(\"document\".\"acl\", NULL) IS NULL AND (cardinality(\"document\".\"acl\") = 0 OR (cardinality(\"document\".\"acl\") = array_length(string_to_array(array_to_string(\"document\".\"acl\", E'\\n'), E'\\n'), 1) AND array_to_string(\"document\".\"acl\", E'\\n') ~ '^((ws|pub|link|u:[^\\nA-Z]+@[^\\nA-Z]+|[gs]:[^\\n:]+:[^\\n:]+:[^\\n]+)(\\n(ws|pub|link|u:[^\\nA-Z]+@[^\\nA-Z]+|[gs]:[^\\n:]+:[^\\n:]+:[^\\n]+))*)$'))" + } + }, + "isRLSEnabled": false + }, + "public.document_secret_provenance": { + "name": "document_secret_provenance", + "schema": "", + "columns": { + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "source_hash": { + "name": "source_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entries": { + "name": "entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "document_secret_provenance_document_id_document_id_fk": { + "name": "document_secret_provenance_document_id_document_id_fk", + "tableFrom": "document_secret_provenance", + "tableTo": "document", + "columnsFrom": ["document_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "document_secret_provenance_status_check": { + "name": "document_secret_provenance_status_check", + "value": "\"document_secret_provenance\".\"status\" IN ('exact', 'unknown')" + } + }, + "isRLSEnabled": false + }, + "public.embedding": { + "name": "embedding", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunk_index": { + "name": "chunk_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "chunk_hash": { + "name": "chunk_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_provenance_version": { + "name": "secret_provenance_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "content_length": { + "name": "content_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "embedding_384": { + "name": "embedding_384", + "type": "vector(384)", + "primaryKey": false, + "notNull": false + }, + "embedding_768": { + "name": "embedding_768", + "type": "vector(768)", + "primaryKey": false, + "notNull": false + }, + "embedding_1024": { + "name": "embedding_1024", + "type": "vector(1024)", + "primaryKey": false, + "notNull": false + }, + "embedding_3072": { + "name": "embedding_3072", + "type": "vector(3072)", + "primaryKey": false, + "notNull": false + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "start_offset": { + "name": "start_offset", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_offset": { + "name": "end_offset", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tag1": { + "name": "tag1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag2": { + "name": "tag2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag3": { + "name": "tag3", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag4": { + "name": "tag4", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag5": { + "name": "tag5", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag6": { + "name": "tag6", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag7": { + "name": "tag7", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "number1": { + "name": "number1", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number2": { + "name": "number2", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number3": { + "name": "number3", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number4": { + "name": "number4", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "number5": { + "name": "number5", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "date1": { + "name": "date1", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "date2": { + "name": "date2", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "boolean1": { + "name": "boolean1", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "boolean2": { + "name": "boolean2", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "boolean3": { + "name": "boolean3", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "to_tsvector('english', \"embedding\".\"content\")", + "type": "stored" + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "emb_doc_chunk_idx": { + "name": "emb_doc_chunk_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chunk_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_model_idx": { + "name": "emb_kb_model_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "embedding_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_enabled_idx": { + "name": "emb_kb_enabled_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_doc_enabled_idx": { + "name": "emb_doc_enabled_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag1_lower_idx": { + "name": "emb_kb_tag1_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag1\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag2_lower_idx": { + "name": "emb_kb_tag2_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag2\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag3_lower_idx": { + "name": "emb_kb_tag3_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag3\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag4_lower_idx": { + "name": "emb_kb_tag4_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag4\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag5_lower_idx": { + "name": "emb_kb_tag5_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag5\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag6_lower_idx": { + "name": "emb_kb_tag6_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag6\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_tag7_lower_idx": { + "name": "emb_kb_tag7_lower_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lower(\"tag7\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_number1_idx": { + "name": "emb_number1_idx", + "columns": [ + { + "expression": "number1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_number2_idx": { + "name": "emb_number2_idx", + "columns": [ + { + "expression": "number2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_number3_idx": { + "name": "emb_number3_idx", + "columns": [ + { + "expression": "number3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_number4_idx": { + "name": "emb_number4_idx", + "columns": [ + { + "expression": "number4", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_number5_idx": { + "name": "emb_number5_idx", + "columns": [ + { + "expression": "number5", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_date1_idx": { + "name": "emb_date1_idx", + "columns": [ + { + "expression": "date1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "emb_date2_idx": { + "name": "emb_date2_idx", + "columns": [ + { + "expression": "date2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "emb_boolean1_idx": { + "name": "emb_boolean1_idx", + "columns": [ + { + "expression": "boolean1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_boolean2_idx": { + "name": "emb_boolean2_idx", + "columns": [ + { + "expression": "boolean2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_boolean3_idx": { + "name": "emb_boolean3_idx", + "columns": [ + { + "expression": "boolean3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_content_fts_idx": { + "name": "emb_content_fts_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_knowledge_base_id_knowledge_base_id_fk": { + "name": "embedding_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "embedding", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "embedding_document_id_document_id_fk": { + "name": "embedding_document_id_document_id_fk", + "tableFrom": "embedding", + "tableTo": "document", + "columnsFrom": ["document_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "embedding_width_check": { + "name": "embedding_width_check", + "value": "num_nonnulls(\"embedding\", \"embedding_384\", \"embedding_768\", \"embedding_1024\", \"embedding_3072\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.embedding_keyword_tin": { + "name": "embedding_keyword_tin", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acl": { + "name": "acl", + "type": "text[]", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "embedding_keyword_tin_document_idx": { + "name": "embedding_keyword_tin_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_tin_id_embedding_id_fk": { + "name": "embedding_keyword_tin_id_embedding_id_fk", + "tableFrom": "embedding_keyword_tin", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.embedding_search": { + "name": "embedding_search", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acl": { + "name": "acl", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "binary": { + "name": "binary", + "type": "bit(1536)", + "primaryKey": false, + "notNull": false + }, + "binary_384": { + "name": "binary_384", + "type": "bit(384)", + "primaryKey": false, + "notNull": false + }, + "binary_768": { + "name": "binary_768", + "type": "bit(768)", + "primaryKey": false, + "notNull": false + }, + "binary_1024": { + "name": "binary_1024", + "type": "bit(1024)", + "primaryKey": false, + "notNull": false + }, + "binary_3072": { + "name": "binary_3072", + "type": "bit(3072)", + "primaryKey": false, + "notNull": false + }, + "vector": { + "name": "vector", + "type": "halfvec(1536)", + "primaryKey": false, + "notNull": false + }, + "vector_384": { + "name": "vector_384", + "type": "halfvec(384)", + "primaryKey": false, + "notNull": false + }, + "vector_512": { + "name": "vector_512", + "type": "halfvec(512)", + "primaryKey": false, + "notNull": false + }, + "vector_768": { + "name": "vector_768", + "type": "halfvec(768)", + "primaryKey": false, + "notNull": false + }, + "vector_1024": { + "name": "vector_1024", + "type": "halfvec(1024)", + "primaryKey": false, + "notNull": false + }, + "vector_3072": { + "name": "vector_3072", + "type": "halfvec(3072)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "embedding_search_kb_idx": { + "name": "embedding_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_search_document_lookup_idx": { + "name": "embedding_search_document_lookup_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"embedding_search\".\"enabled\"", + "concurrently": true, + "method": "btree", + "with": {} + }, + "embedding_search_cosine_hnsw_idx": { + "name": "embedding_search_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "embedding_search_512_cosine_hnsw_idx": { + "name": "embedding_search_512_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector_512", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "embedding_search_384_cosine_hnsw_idx": { + "name": "embedding_search_384_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector_384", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "embedding_search_768_cosine_hnsw_idx": { + "name": "embedding_search_768_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector_768", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "embedding_search_1024_cosine_hnsw_idx": { + "name": "embedding_search_1024_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector_1024", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "embedding_search_3072_cosine_hnsw_idx": { + "name": "embedding_search_3072_cosine_hnsw_idx", + "columns": [ + { + "expression": "vector_3072", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "halfvec_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + } + }, + "foreignKeys": { + "embedding_search_id_embedding_id_fk": { + "name": "embedding_search_id_embedding_id_fk", + "tableFrom": "embedding_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "embedding_search_width_check": { + "name": "embedding_search_width_check", + "value": "num_nonnulls(\"binary\", \"binary_384\", \"binary_768\", \"binary_1024\", \"binary_3072\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.embedding_secret_provenance": { + "name": "embedding_secret_provenance", + "schema": "", + "columns": { + "embedding_id": { + "name": "embedding_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entries": { + "name": "entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "embedding_secret_provenance_embedding_id_embedding_id_fk": { + "name": "embedding_secret_provenance_embedding_id_embedding_id_fk", + "tableFrom": "embedding_secret_provenance", + "tableTo": "embedding", + "columnsFrom": ["embedding_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "embedding_secret_provenance_status_check": { + "name": "embedding_secret_provenance_status_check", + "value": "\"embedding_secret_provenance\".\"status\" IN ('exact', 'unknown')" + } + }, + "isRLSEnabled": false + }, + "public.environment": { + "name": "environment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "environment_user_id_user_id_fk": { + "name": "environment_user_id_user_id_fk", + "tableFrom": "environment", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "environment_user_id_unique": { + "name": "environment_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.execution_large_value_dependencies": { + "name": "execution_large_value_dependencies", + "schema": "", + "columns": { + "parent_key": { + "name": "parent_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "child_key": { + "name": "child_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "execution_large_value_dependencies_workspace_parent_key_idx": { + "name": "execution_large_value_dependencies_workspace_parent_key_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_large_value_dependencies_workspace_child_key_idx": { + "name": "execution_large_value_dependencies_workspace_child_key_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "child_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_large_value_dependencies_workspace_id_workspace_id_fk": { + "name": "execution_large_value_dependencies_workspace_id_workspace_id_fk", + "tableFrom": "execution_large_value_dependencies", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "execution_large_value_dependencies_parent_key_child_key_pk": { + "name": "execution_large_value_dependencies_parent_key_child_key_pk", + "columns": ["parent_key", "child_key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.execution_large_value_references": { + "name": "execution_large_value_references", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "execution_large_value_reference_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "execution_large_value_references_workspace_execution_source_idx": { + "name": "execution_large_value_references_workspace_execution_source_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_large_value_references_workflow_id_idx": { + "name": "execution_large_value_references_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_large_value_references_workspace_id_workspace_id_fk": { + "name": "execution_large_value_references_workspace_id_workspace_id_fk", + "tableFrom": "execution_large_value_references", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "execution_large_value_references_workflow_id_workflow_id_fk": { + "name": "execution_large_value_references_workflow_id_workflow_id_fk", + "tableFrom": "execution_large_value_references", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "execution_large_value_references_key_execution_id_source_pk": { + "name": "execution_large_value_references_key_execution_id_source_pk", + "columns": ["key", "execution_id", "source"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.execution_large_values": { + "name": "execution_large_values", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_execution_id": { + "name": "owner_execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "execution_large_values_owner_execution_id_idx": { + "name": "execution_large_values_owner_execution_id_idx", + "columns": [ + { + "expression": "owner_execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_large_values_cleanup_idx": { + "name": "execution_large_values_cleanup_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"execution_large_values\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_large_values_tombstone_cleanup_idx": { + "name": "execution_large_values_tombstone_cleanup_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"execution_large_values\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_large_values_workflow_id_idx": { + "name": "execution_large_values_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_large_values_workspace_id_workspace_id_fk": { + "name": "execution_large_values_workspace_id_workspace_id_fk", + "tableFrom": "execution_large_values", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "execution_large_values_workflow_id_workflow_id_fk": { + "name": "execution_large_values_workflow_id_workflow_id_fk", + "tableFrom": "execution_large_values", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.folder": { + "name": "folder", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "folder_resource_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "locked": { + "name": "locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "folder_user_idx": { + "name": "folder_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "folder_workspace_resource_parent_idx": { + "name": "folder_workspace_resource_parent_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "folder_parent_sort_idx": { + "name": "folder_parent_sort_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "folder_deleted_at_idx": { + "name": "folder_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "folder_workspace_deleted_partial_idx": { + "name": "folder_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"folder\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "folder_workspace_resource_parent_name_active_unique": { + "name": "folder_workspace_resource_parent_name_active_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"parent_id\", '')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"folder\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "folder_user_id_user_id_fk": { + "name": "folder_user_id_user_id_fk", + "tableFrom": "folder", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "folder_workspace_id_workspace_id_fk": { + "name": "folder_workspace_id_workspace_id_fk", + "tableFrom": "folder", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "folder_parent_id_folder_id_fk": { + "name": "folder_parent_id_folder_id_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.idempotency_key": { + "name": "idempotency_key", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "result": { + "name": "result", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idempotency_key_created_at_idx": { + "name": "idempotency_key_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "invitation_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'organization'" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "membership_intent": { + "name": "membership_intent", + "type": "invitation_membership_intent", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "invitation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "invitation_email_idx": { + "name": "invitation_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_organization_id_idx": { + "name": "invitation_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_status_idx": { + "name": "invitation_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_pending_email_org_unique": { + "name": "invitation_pending_email_org_unique", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"invitation\".\"status\" = 'pending' AND \"invitation\".\"organization_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": ["inviter_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "invitation_token_unique": { + "name": "invitation_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation_workspace_grant": { + "name": "invitation_workspace_grant", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "invitation_id": { + "name": "invitation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "invitation_workspace_grant_unique": { + "name": "invitation_workspace_grant_unique", + "columns": [ + { + "expression": "invitation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_workspace_grant_workspace_id_idx": { + "name": "invitation_workspace_grant_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "invitation_workspace_grant_invitation_id_invitation_id_fk": { + "name": "invitation_workspace_grant_invitation_id_invitation_id_fk", + "tableFrom": "invitation_workspace_grant", + "tableTo": "invitation", + "columnsFrom": ["invitation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_workspace_grant_workspace_id_workspace_id_fk": { + "name": "invitation_workspace_grant_workspace_id_workspace_id_fk", + "tableFrom": "invitation_workspace_grant", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.job_execution_logs": { + "name": "job_execution_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule_id": { + "name": "schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_duration_ms": { + "name": "total_duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "execution_data": { + "name": "execution_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "cost": { + "name": "cost", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "job_execution_logs_schedule_id_idx": { + "name": "job_execution_logs_schedule_id_idx", + "columns": [ + { + "expression": "schedule_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_execution_logs_workspace_started_at_idx": { + "name": "job_execution_logs_workspace_started_at_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_execution_logs_workspace_ended_at_id_idx": { + "name": "job_execution_logs_workspace_ended_at_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_trunc('milliseconds', \"ended_at\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_execution_logs_execution_id_unique": { + "name": "job_execution_logs_execution_id_unique", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_execution_logs_trigger_idx": { + "name": "job_execution_logs_trigger_idx", + "columns": [ + { + "expression": "trigger", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "job_execution_logs_schedule_id_workflow_schedule_id_fk": { + "name": "job_execution_logs_schedule_id_workflow_schedule_id_fk", + "tableFrom": "job_execution_logs", + "tableTo": "workflow_schedule", + "columnsFrom": ["schedule_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "job_execution_logs_workspace_id_workspace_id_fk": { + "name": "job_execution_logs_workspace_id_workspace_id_fk", + "tableFrom": "job_execution_logs", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_base": { + "name": "knowledge_base", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "folder_id": { + "name": "folder_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_search_index": { + "name": "is_search_index", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "embedding_dimension": { + "name": "embedding_dimension", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1536 + }, + "chunking_config": { + "name": "chunking_config", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{\"maxSize\": 1024, \"minSize\": 1, \"overlap\": 200}'" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kb_organization_id_idx": { + "name": "kb_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_organization_search_index_unique": { + "name": "kb_organization_search_index_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"knowledge_base\".\"is_search_index\" = true AND \"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_organization_name_active_unique": { + "name": "kb_organization_name_active_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_user_id_idx": { + "name": "kb_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_workspace_id_idx": { + "name": "kb_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_user_workspace_idx": { + "name": "kb_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_folder_id_idx": { + "name": "kb_folder_id_idx", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_deleted_at_idx": { + "name": "kb_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_workspace_deleted_partial_idx": { + "name": "kb_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_base\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_workspace_name_active_unique": { + "name": "kb_workspace_name_active_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_workspace_search_index_unique": { + "name": "kb_workspace_search_index_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"knowledge_base\".\"is_search_index\" = true AND \"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_base_user_id_user_id_fk": { + "name": "knowledge_base_user_id_user_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_base_workspace_id_workspace_id_fk": { + "name": "knowledge_base_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_base_organization_id_organization_id_fk": { + "name": "knowledge_base_organization_id_organization_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_base_folder_id_folder_id_fk": { + "name": "knowledge_base_folder_id_folder_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "folder", + "columnsFrom": ["folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kb_owner_check": { + "name": "kb_owner_check", + "value": "num_nonnulls(\"knowledge_base\".\"workspace_id\", \"knowledge_base\".\"organization_id\") = 1" + }, + "kb_organization_search_index_check": { + "name": "kb_organization_search_index_check", + "value": "\"knowledge_base\".\"organization_id\" IS NULL OR \"knowledge_base\".\"is_search_index\"" + }, + "kb_organization_folder_check": { + "name": "kb_organization_folder_check", + "value": "\"knowledge_base\".\"organization_id\" IS NULL OR \"knowledge_base\".\"folder_id\" IS NULL" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_base_tag_definitions": { + "name": "knowledge_base_tag_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tag_slot": { + "name": "tag_slot", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "field_type": { + "name": "field_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kb_tag_definitions_kb_slot_idx": { + "name": "kb_tag_definitions_kb_slot_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tag_slot", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_tag_definitions_kb_display_name_idx": { + "name": "kb_tag_definitions_kb_display_name_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_tag_definitions_kb_id_idx": { + "name": "kb_tag_definitions_kb_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_base_tag_definitions_knowledge_base_id_knowledge_base_id_fk": { + "name": "knowledge_base_tag_definitions_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "knowledge_base_tag_definitions", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_connector": { + "name": "knowledge_connector", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_type": { + "name": "connector_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_config": { + "name": "source_config", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "sync_mode": { + "name": "sync_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'full'" + }, + "sync_interval_minutes": { + "name": "sync_interval_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1440 + }, + "access_mode": { + "name": "access_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'workspace'" + }, + "credential_group_id": { + "name": "credential_group_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_group_option_id": { + "name": "credential_group_option_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "member_sync_status": { + "name": "member_sync_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "member_sync_lock_token": { + "name": "member_sync_lock_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "member_sync_lock_lease_at": { + "name": "member_sync_lock_lease_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "next_member_sync_at": { + "name": "next_member_sync_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + }, + "last_member_sync_at": { + "name": "last_member_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_member_sync_error": { + "name": "last_member_sync_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "member_sync_consecutive_failures": { + "name": "member_sync_consecutive_failures", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "access_rewrite_pending": { + "name": "access_rewrite_pending", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "member_tombstone_cursor": { + "name": "member_tombstone_cursor", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "member_resurrection_cursor": { + "name": "member_resurrection_cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_sync_error": { + "name": "last_sync_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_sync_doc_count": { + "name": "last_sync_doc_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "listing_checkpoint": { + "name": "listing_checkpoint", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "directory_checkpoint": { + "name": "directory_checkpoint", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "next_sync_at": { + "name": "next_sync_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + }, + "next_directory_sync_at": { + "name": "next_directory_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "consecutive_failures": { + "name": "consecutive_failures", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sync_lock_token": { + "name": "sync_lock_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sync_lock_lease_at": { + "name": "sync_lock_lease_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "detached_at": { + "name": "detached_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "detach_reserved_bytes": { + "name": "detach_reserved_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "kc_knowledge_base_id_idx": { + "name": "kc_knowledge_base_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kc_status_next_sync_idx": { + "name": "kc_status_next_sync_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_sync_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kc_archived_at_partial_idx": { + "name": "kc_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kc_deleted_at_partial_idx": { + "name": "kc_deleted_at_partial_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kc_member_sync_due_idx": { + "name": "kc_member_sync_due_idx", + "columns": [ + { + "expression": "member_sync_status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_member_sync_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector\".\"access_mode\" = 'members' AND \"knowledge_connector\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "kc_directory_sync_due_idx": { + "name": "kc_directory_sync_due_idx", + "columns": [ + { + "expression": "next_directory_sync_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector\".\"access_mode\" = 'admin' AND \"knowledge_connector\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_connector_knowledge_base_id_knowledge_base_id_fk": { + "name": "knowledge_connector_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "knowledge_connector", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_connector_credential_group_id_credential_group_id_fk": { + "name": "knowledge_connector_credential_group_id_credential_group_id_fk", + "tableFrom": "knowledge_connector", + "tableTo": "credential_group", + "columnsFrom": ["credential_group_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kc_access_mode_check": { + "name": "kc_access_mode_check", + "value": "\"knowledge_connector\".\"access_mode\" IN ('workspace', 'members', 'admin')" + }, + "kc_member_sync_status_check": { + "name": "kc_member_sync_status_check", + "value": "\"knowledge_connector\".\"member_sync_status\" IN ('idle', 'pending', 'running', 'error', 'disabled')" + }, + "kc_sync_lock_exclusive_check": { + "name": "kc_sync_lock_exclusive_check", + "value": "NOT (\"knowledge_connector\".\"sync_lock_token\" IS NOT NULL AND \"knowledge_connector\".\"member_sync_lock_token\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_member": { + "name": "knowledge_connector_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject_token": { + "name": "subject_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "consecutive_failures": { + "name": "consecutive_failures", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_started_at": { + "name": "last_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_complete_listing_at": { + "name": "last_complete_listing_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_listed_count": { + "name": "last_listed_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "member_synced_through": { + "name": "member_synced_through", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope_renewed_at": { + "name": "scope_renewed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope_renewal_cursor": { + "name": "scope_renewal_cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope_renewal_started_at": { + "name": "scope_renewal_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "change_cursor": { + "name": "change_cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "listing_checkpoint": { + "name": "listing_checkpoint", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "suspended_at": { + "name": "suspended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kcm_organization_id_idx": { + "name": "kcm_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcm_connector_credential_unique": { + "name": "kcm_connector_credential_unique", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcm_connector_queue_idx": { + "name": "kcm_connector_queue_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_attempt_at", + "isExpression": false, + "asc": true, + "nulls": "first" + }, + { + "expression": "last_started_at", + "isExpression": false, + "asc": true, + "nulls": "first" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcm_credential_idx": { + "name": "kcm_credential_idx", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_connector_member_workspace_id_workspace_id_fk": { + "name": "knowledge_connector_member_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_connector_member", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_connector_member_organization_id_organization_id_fk": { + "name": "knowledge_connector_member_organization_id_organization_id_fk", + "tableFrom": "knowledge_connector_member", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_connector_member_connector_id_knowledge_connector_id_fk": { + "name": "knowledge_connector_member_connector_id_knowledge_connector_id_fk", + "tableFrom": "knowledge_connector_member", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_connector_member_credential_id_credential_id_fk": { + "name": "knowledge_connector_member_credential_id_credential_id_fk", + "tableFrom": "knowledge_connector_member", + "tableTo": "credential", + "columnsFrom": ["credential_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcm_owner_check": { + "name": "kcm_owner_check", + "value": "num_nonnulls(\"knowledge_connector_member\".\"workspace_id\", \"knowledge_connector_member\".\"organization_id\") = 1" + }, + "kcm_status_check": { + "name": "kcm_status_check", + "value": "\"knowledge_connector_member\".\"status\" IN ('active', 'suspended', 'disabled')" + }, + "kcm_subject_token_shape_check": { + "name": "kcm_subject_token_shape_check", + "value": "\"knowledge_connector_member\".\"subject_token\" ~ '^s:[^:]+:[^:]+:.+$'" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_member_sync_log": { + "name": "knowledge_connector_member_sync_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "members_claimed": { + "name": "members_claimed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "members_completed": { + "name": "members_completed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "members_incomplete": { + "name": "members_incomplete", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "members_failed": { + "name": "members_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_failed": { + "name": "docs_failed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "processing_dispatch_failed": { + "name": "processing_dispatch_failed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "docs_listed": { + "name": "docs_listed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_added": { + "name": "docs_added", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_updated": { + "name": "docs_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_unchanged": { + "name": "docs_unchanged", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_hydrated_once": { + "name": "docs_hydrated_once", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "observations_added": { + "name": "observations_added", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "observations_renewed": { + "name": "observations_renewed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "observations_removed": { + "name": "observations_removed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_tombstoned": { + "name": "docs_tombstoned", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_resurrected": { + "name": "docs_resurrected", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_purged": { + "name": "docs_purged", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "credentials_audited": { + "name": "credentials_audited", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "database_failure_class": { + "name": "database_failure_class", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "kcmsl_connector_started_at_idx": { + "name": "kcmsl_connector_started_at_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"started_at\" DESC", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcmsl_started_at_partial_idx": { + "name": "kcmsl_started_at_partial_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector_member_sync_log\".\"status\" = 'started'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_connector_member_sync_log_connector_id_knowledge_connector_id_fk": { + "name": "knowledge_connector_member_sync_log_connector_id_knowledge_connector_id_fk", + "tableFrom": "knowledge_connector_member_sync_log", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcmsl_status_check": { + "name": "kcmsl_status_check", + "value": "\"knowledge_connector_member_sync_log\".\"status\" IN ('started', 'partial', 'completed', 'failed')" + }, + "kcmsl_database_failure_class_check": { + "name": "kcmsl_database_failure_class_check", + "value": "\"knowledge_connector_member_sync_log\".\"database_failure_class\" IN ('capacity', 'conflict', 'connection')" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_partition": { + "name": "knowledge_connector_partition", + "schema": "", + "columns": { + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "partition_key": { + "name": "partition_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "generation_id": { + "name": "generation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "context": { + "name": "context", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "cursor": { + "name": "cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "retry_at": { + "name": "retry_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_served_at": { + "name": "last_served_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "failure": { + "name": "failure", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "permission_cursor": { + "name": "permission_cursor", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permission_attempts": { + "name": "permission_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "permission_retry_at": { + "name": "permission_retry_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permission_last_served_at": { + "name": "permission_last_served_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "permission_started_at": { + "name": "permission_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "permission_failure": { + "name": "permission_failure", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "kcp_content_due_idx": { + "name": "kcp_content_due_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "retry_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_served_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcp_permission_due_idx": { + "name": "kcp_permission_due_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission_retry_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission_last_served_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_connector_partition_connector_id_knowledge_connector_id_fk": { + "name": "knowledge_connector_partition_connector_id_knowledge_connector_id_fk", + "tableFrom": "knowledge_connector_partition", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "kcp_pk": { + "name": "kcp_pk", + "columns": ["connector_id", "partition_key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcp_partition_key_check": { + "name": "kcp_partition_key_check", + "value": "octet_length(\"knowledge_connector_partition\".\"partition_key\") BETWEEN 1 AND 1024" + }, + "kcp_context_check": { + "name": "kcp_context_check", + "value": "jsonb_typeof(\"knowledge_connector_partition\".\"context\") = 'object' AND octet_length(\"knowledge_connector_partition\".\"context\"::text) <= 16384" + }, + "kcp_status_check": { + "name": "kcp_status_check", + "value": "\"knowledge_connector_partition\".\"status\" IN ('pending', 'complete', 'blocked')" + }, + "kcp_cursor_check": { + "name": "kcp_cursor_check", + "value": "(\"knowledge_connector_partition\".\"cursor\" IS NULL OR octet_length(\"knowledge_connector_partition\".\"cursor\") <= 393216) AND (\"knowledge_connector_partition\".\"permission_cursor\" IS NULL OR octet_length(\"knowledge_connector_partition\".\"permission_cursor\") <= 393216)" + }, + "kcp_attempts_check": { + "name": "kcp_attempts_check", + "value": "\"knowledge_connector_partition\".\"attempts\" >= 0 AND \"knowledge_connector_partition\".\"permission_attempts\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_permission_grant": { + "name": "knowledge_connector_permission_grant", + "schema": "", + "columns": { + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "group_key": { + "name": "group_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject_token": { + "name": "subject_token", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "kcpg_subject_idx": { + "name": "kcpg_subject_idx", + "columns": [ + { + "expression": "subject_token", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "group_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kcpg_snapshot_fk": { + "name": "kcpg_snapshot_fk", + "tableFrom": "knowledge_connector_permission_grant", + "tableTo": "knowledge_connector_permission_snapshot", + "columnsFrom": ["connector_id"], + "columnsTo": ["connector_id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "kcpg_pk": { + "name": "kcpg_pk", + "columns": ["connector_id", "group_key", "subject_token"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcpg_group_check": { + "name": "kcpg_group_check", + "value": "length(\"knowledge_connector_permission_grant\".\"group_key\") BETWEEN 1 AND 255" + }, + "kcpg_subject_check": { + "name": "kcpg_subject_check", + "value": "\"knowledge_connector_permission_grant\".\"subject_token\" ~ '^u:[^[:space:]A-Z]+@[^[:space:]A-Z]+$'" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_permission_snapshot": { + "name": "knowledge_connector_permission_snapshot", + "schema": "", + "columns": { + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "kcps_connector_fk": { + "name": "kcps_connector_fk", + "tableFrom": "knowledge_connector_permission_snapshot", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcps_revision_check": { + "name": "kcps_revision_check", + "value": "\"knowledge_connector_permission_snapshot\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_connector_sync_log": { + "name": "knowledge_connector_sync_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "docs_added": { + "name": "docs_added", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_updated": { + "name": "docs_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_deleted": { + "name": "docs_deleted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_unchanged": { + "name": "docs_unchanged", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_skipped": { + "name": "docs_skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "docs_failed": { + "name": "docs_failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "listed_count": { + "name": "listed_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "database_failure_class": { + "name": "database_failure_class", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "kcsl_connector_started_at_idx": { + "name": "kcsl_connector_started_at_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"started_at\" DESC", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kcsl_started_at_partial_idx": { + "name": "kcsl_started_at_partial_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"knowledge_connector_sync_log\".\"status\" = 'started'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_connector_sync_log_connector_id_knowledge_connector_id_fk": { + "name": "knowledge_connector_sync_log_connector_id_knowledge_connector_id_fk", + "tableFrom": "knowledge_connector_sync_log", + "tableTo": "knowledge_connector", + "columnsFrom": ["connector_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kcsl_database_failure_class_check": { + "name": "kcsl_database_failure_class_check", + "value": "\"knowledge_connector_sync_log\".\"database_failure_class\" IN ('capacity', 'conflict', 'connection')" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_document_observation": { + "name": "knowledge_document_observation", + "schema": "", + "columns": { + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "member_id": { + "name": "member_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "kdo_member_idx": { + "name": "kdo_member_idx", + "columns": [ + { + "expression": "member_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_document_observation_document_id_document_id_fk": { + "name": "knowledge_document_observation_document_id_document_id_fk", + "tableFrom": "knowledge_document_observation", + "tableTo": "document", + "columnsFrom": ["document_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_document_observation_member_id_knowledge_connector_member_id_fk": { + "name": "knowledge_document_observation_member_id_knowledge_connector_member_id_fk", + "tableFrom": "knowledge_document_observation", + "tableTo": "knowledge_connector_member", + "columnsFrom": ["member_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "knowledge_document_observation_document_id_member_id_pk": { + "name": "knowledge_document_observation_document_id_member_id_pk", + "columns": ["document_id", "member_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_external_directory": { + "name": "knowledge_external_directory", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sync_lock_token": { + "name": "sync_lock_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sync_lock_lease_at": { + "name": "sync_lock_lease_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_started_at": { + "name": "last_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_complete_sync_at": { + "name": "last_complete_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "ked_organization_id_idx": { + "name": "ked_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ked_workspace_identity_unique": { + "name": "ked_workspace_identity_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ked_organization_identity_unique": { + "name": "ked_organization_identity_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_external_directory_workspace_id_workspace_id_fk": { + "name": "knowledge_external_directory_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_external_directory", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_external_directory_organization_id_organization_id_fk": { + "name": "knowledge_external_directory_organization_id_organization_id_fk", + "tableFrom": "knowledge_external_directory", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "ked_owner_check": { + "name": "ked_owner_check", + "value": "num_nonnulls(\"knowledge_external_directory\".\"workspace_id\", \"knowledge_external_directory\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_external_group": { + "name": "knowledge_external_group", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_group_id": { + "name": "external_group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "keg_organization_id_idx": { + "name": "keg_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "keg_organization_identity_unique": { + "name": "keg_organization_identity_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "keg_organization_synced_idx": { + "name": "keg_organization_synced_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_synced_at", + "isExpression": false, + "asc": true, + "nulls": "first" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "keg_identity_unique": { + "name": "keg_identity_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "keg_workspace_synced_idx": { + "name": "keg_workspace_synced_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_synced_at", + "isExpression": false, + "asc": true, + "nulls": "first" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_external_group_organization_id_organization_id_fk": { + "name": "knowledge_external_group_organization_id_organization_id_fk", + "tableFrom": "knowledge_external_group", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "keg_workspace_fk": { + "name": "keg_workspace_fk", + "tableFrom": "knowledge_external_group", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "keg_owner_check": { + "name": "keg_owner_check", + "value": "num_nonnulls(\"knowledge_external_group\".\"workspace_id\", \"knowledge_external_group\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.knowledge_external_group_member": { + "name": "knowledge_external_group_member", + "schema": "", + "columns": { + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subject_token": { + "name": "subject_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kegm_subject_token_idx": { + "name": "kegm_subject_token_idx", + "columns": [ + { + "expression": "subject_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kegm_group_fk": { + "name": "kegm_group_fk", + "tableFrom": "knowledge_external_group_member", + "tableTo": "knowledge_external_group", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "knowledge_external_group_member_group_id_subject_token_pk": { + "name": "knowledge_external_group_member_group_id_subject_token_pk", + "columns": ["group_id", "subject_token"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_projection_dirty": { + "name": "knowledge_projection_dirty", + "schema": "", + "columns": { + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "content": { + "name": "content", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "marked_at": { + "name": "marked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "knowledge_projection_dirty_marked_at_idx": { + "name": "knowledge_projection_dirty_marked_at_idx", + "columns": [ + { + "expression": "marked_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_projection_dirty_document_id_document_id_fk": { + "name": "knowledge_projection_dirty_document_id_document_id_fk", + "tableFrom": "knowledge_projection_dirty", + "tableTo": "document", + "columnsFrom": ["document_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mcp_server_oauth": { + "name": "mcp_server_oauth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mcp_server_id": { + "name": "mcp_server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_information": { + "name": "client_information", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tokens": { + "name": "tokens", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state_created_at": { + "name": "state_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_refreshed_at": { + "name": "last_refreshed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "mcp_server_oauth_server_unique": { + "name": "mcp_server_oauth_server_unique", + "columns": [ + { + "expression": "mcp_server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_server_oauth_state_idx": { + "name": "mcp_server_oauth_state_idx", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_server_oauth_mcp_server_id_mcp_servers_id_fk": { + "name": "mcp_server_oauth_mcp_server_id_mcp_servers_id_fk", + "tableFrom": "mcp_server_oauth", + "tableTo": "mcp_servers", + "columnsFrom": ["mcp_server_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_server_oauth_user_id_user_id_fk": { + "name": "mcp_server_oauth_user_id_user_id_fk", + "tableFrom": "mcp_server_oauth", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "mcp_server_oauth_workspace_id_workspace_id_fk": { + "name": "mcp_server_oauth_workspace_id_workspace_id_fk", + "tableFrom": "mcp_server_oauth", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_server_oauth_organization_id_organization_id_fk": { + "name": "mcp_server_oauth_organization_id_organization_id_fk", + "tableFrom": "mcp_server_oauth", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_server_oauth_owner_check": { + "name": "mcp_server_oauth_owner_check", + "value": "num_nonnulls(\"mcp_server_oauth\".\"workspace_id\", \"mcp_server_oauth\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.mcp_servers": { + "name": "mcp_servers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_group_id": { + "name": "credential_group_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "managed_connector_id": { + "name": "managed_connector_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_config_version": { + "name": "oauth_config_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "transport": { + "name": "transport", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'headers'" + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_client_secret": { + "name": "oauth_client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "headers": { + "name": "headers", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "timeout": { + "name": "timeout", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 30000 + }, + "retries": { + "name": "retries", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "last_connected": { + "name": "last_connected", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "connection_status": { + "name": "connection_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'disconnected'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_config": { + "name": "status_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "tool_count": { + "name": "tool_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_tools_refresh": { + "name": "last_tools_refresh", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_requests": { + "name": "total_requests", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_used": { + "name": "last_used", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "mcp_servers_organization_id_idx": { + "name": "mcp_servers_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_servers_workspace_enabled_idx": { + "name": "mcp_servers_workspace_enabled_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_servers_credential_group_idx": { + "name": "mcp_servers_credential_group_idx", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_servers_credential_group_managed_connector_unique": { + "name": "mcp_servers_credential_group_managed_connector_unique", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "managed_connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"mcp_servers\".\"credential_group_id\" IS NOT NULL AND \"mcp_servers\".\"managed_connector_id\" IS NOT NULL AND \"mcp_servers\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_servers_workspace_deleted_partial_idx": { + "name": "mcp_servers_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"mcp_servers\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_servers_workspace_id_workspace_id_fk": { + "name": "mcp_servers_workspace_id_workspace_id_fk", + "tableFrom": "mcp_servers", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_servers_organization_id_organization_id_fk": { + "name": "mcp_servers_organization_id_organization_id_fk", + "tableFrom": "mcp_servers", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_servers_credential_group_id_credential_group_id_fk": { + "name": "mcp_servers_credential_group_id_credential_group_id_fk", + "tableFrom": "mcp_servers", + "tableTo": "credential_group", + "columnsFrom": ["credential_group_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "mcp_servers_created_by_user_id_fk": { + "name": "mcp_servers_created_by_user_id_fk", + "tableFrom": "mcp_servers", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_servers_owner_check": { + "name": "mcp_servers_owner_check", + "value": "num_nonnulls(\"mcp_servers\".\"workspace_id\", \"mcp_servers\".\"organization_id\") = 1" + }, + "mcp_servers_organization_managed_check": { + "name": "mcp_servers_organization_managed_check", + "value": "\"mcp_servers\".\"organization_id\" IS NULL OR \"mcp_servers\".\"credential_group_id\" IS NOT NULL" + }, + "mcp_servers_credential_group_managed_connector_check": { + "name": "mcp_servers_credential_group_managed_connector_check", + "value": "\"mcp_servers\".\"credential_group_id\" IS NULL OR \"mcp_servers\".\"managed_connector_id\" IS NOT NULL" + }, + "mcp_servers_managed_connector_oauth_check": { + "name": "mcp_servers_managed_connector_oauth_check", + "value": "\"mcp_servers\".\"managed_connector_id\" IS NULL OR \"mcp_servers\".\"auth_type\" = 'oauth'" + } + }, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "member_user_id_unique": { + "name": "member_user_id_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "member_organization_id_idx": { + "name": "member_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory": { + "name": "memory", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "storage_version": { + "name": "storage_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "encrypted_context_summary": { + "name": "encrypted_context_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_provenance_version": { + "name": "secret_provenance_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "memory_key_idx": { + "name": "memory_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_workspace_idx": { + "name": "memory_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_workspace_key_idx": { + "name": "memory_workspace_key_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_workspace_deleted_partial_idx": { + "name": "memory_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"memory\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_workspace_id_workspace_id_fk": { + "name": "memory_workspace_id_workspace_id_fk", + "tableFrom": "memory", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_artifact": { + "name": "memory_artifact", + "schema": "", + "columns": { + "memory_id": { + "name": "memory_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "memory_artifact_key_idx": { + "name": "memory_artifact_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_artifact_memory_id_memory_id_fk": { + "name": "memory_artifact_memory_id_memory_id_fk", + "tableFrom": "memory_artifact", + "tableTo": "memory", + "columnsFrom": ["memory_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memory_artifact_key_execution_large_values_key_fk": { + "name": "memory_artifact_key_execution_large_values_key_fk", + "tableFrom": "memory_artifact", + "tableTo": "execution_large_values", + "columnsFrom": ["key"], + "columnsTo": ["key"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "memory_artifact_memory_id_key_pk": { + "name": "memory_artifact_memory_id_key_pk", + "columns": ["memory_id", "key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_item": { + "name": "memory_item", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "memory_id": { + "name": "memory_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "identity": { + "type": "always", + "name": "memory_item_sequence_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "append_key": { + "name": "append_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "turn_id": { + "name": "turn_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provenance_status": { + "name": "provenance_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provenance_entries": { + "name": "provenance_entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "memory_item_append_unique": { + "name": "memory_item_append_unique", + "columns": [ + { + "expression": "memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "append_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_item_sequence_idx": { + "name": "memory_item_sequence_idx", + "columns": [ + { + "expression": "memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_item_memory_id_memory_id_fk": { + "name": "memory_item_memory_id_memory_id_fk", + "tableFrom": "memory_item", + "tableTo": "memory", + "columnsFrom": ["memory_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "memory_item_kind_check": { + "name": "memory_item_kind_check", + "value": "\"memory_item\".\"kind\" IN ('message', 'exchange')" + }, + "memory_item_provenance_status_check": { + "name": "memory_item_provenance_status_check", + "value": "\"memory_item\".\"provenance_status\" IN ('exact', 'unknown')" + } + }, + "isRLSEnabled": false + }, + "public.memory_secret_provenance": { + "name": "memory_secret_provenance", + "schema": "", + "columns": { + "memory_id": { + "name": "memory_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entries": { + "name": "entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "memory_secret_provenance_memory_id_memory_id_fk": { + "name": "memory_secret_provenance_memory_id_memory_id_fk", + "tableFrom": "memory_secret_provenance", + "tableTo": "memory", + "columnsFrom": ["memory_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "memory_secret_provenance_status_check": { + "name": "memory_secret_provenance_status_check", + "value": "\"memory_secret_provenance\".\"status\" IN ('exact', 'unknown')" + } + }, + "isRLSEnabled": false + }, + "public.mothership_inbox_allowed_sender": { + "name": "mothership_inbox_allowed_sender", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "added_by": { + "name": "added_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inbox_sender_ws_email_idx": { + "name": "inbox_sender_ws_email_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mothership_inbox_allowed_sender_workspace_id_workspace_id_fk": { + "name": "mothership_inbox_allowed_sender_workspace_id_workspace_id_fk", + "tableFrom": "mothership_inbox_allowed_sender", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mothership_inbox_allowed_sender_added_by_user_id_fk": { + "name": "mothership_inbox_allowed_sender_added_by_user_id_fk", + "tableFrom": "mothership_inbox_allowed_sender", + "tableTo": "user", + "columnsFrom": ["added_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mothership_inbox_task": { + "name": "mothership_inbox_task", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_email": { + "name": "from_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_name": { + "name": "from_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_preview": { + "name": "body_preview", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body_text": { + "name": "body_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body_html": { + "name": "body_html", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_message_id": { + "name": "email_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "in_reply_to": { + "name": "in_reply_to", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_message_id": { + "name": "response_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agentmail_message_id": { + "name": "agentmail_message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'received'" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "trigger_job_id": { + "name": "trigger_job_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_summary": { + "name": "result_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rejection_reason": { + "name": "rejection_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "has_attachments": { + "name": "has_attachments", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cc_recipients": { + "name": "cc_recipients", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "inbox_task_ws_created_at_idx": { + "name": "inbox_task_ws_created_at_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_task_ws_status_idx": { + "name": "inbox_task_ws_status_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_task_response_msg_id_idx": { + "name": "inbox_task_response_msg_id_idx", + "columns": [ + { + "expression": "response_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_task_email_msg_id_idx": { + "name": "inbox_task_email_msg_id_idx", + "columns": [ + { + "expression": "email_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mothership_inbox_task_workspace_id_workspace_id_fk": { + "name": "mothership_inbox_task_workspace_id_workspace_id_fk", + "tableFrom": "mothership_inbox_task", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mothership_inbox_task_chat_id_copilot_chats_id_fk": { + "name": "mothership_inbox_task_chat_id_copilot_chats_id_fk", + "tableFrom": "mothership_inbox_task", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mothership_inbox_webhook": { + "name": "mothership_inbox_webhook", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "webhook_id": { + "name": "webhook_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "mothership_inbox_webhook_workspace_id_workspace_id_fk": { + "name": "mothership_inbox_webhook_workspace_id_workspace_id_fk", + "tableFrom": "mothership_inbox_webhook", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mothership_inbox_webhook_workspace_id_unique": { + "name": "mothership_inbox_webhook_workspace_id_unique", + "nullsNotDistinct": false, + "columns": ["workspace_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mothership_resource_effects": { + "name": "mothership_resource_effects", + "schema": "", + "columns": { + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "effect_id": { + "name": "effect_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "mothership_resource_effects_chat_id_copilot_chats_id_fk": { + "name": "mothership_resource_effects_chat_id_copilot_chats_id_fk", + "tableFrom": "mothership_resource_effects", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "mothership_resource_effects_chat_id_effect_id_pk": { + "name": "mothership_resource_effects_chat_id_effect_id_pk", + "columns": ["chat_id", "effect_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mothership_settings": { + "name": "mothership_settings", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mcp_tool_refs": { + "name": "mcp_tool_refs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "custom_tool_refs": { + "name": "custom_tool_refs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "skill_refs": { + "name": "skill_refs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "mothership_settings_workspace_id_workspace_id_fk": { + "name": "mothership_settings_workspace_id_workspace_id_fk", + "tableFrom": "mothership_settings", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_access_token": { + "name": "oauth_access_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_id": { + "name": "refresh_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "resource": { + "name": "resource", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "oauth_access_token_client_id_idx": { + "name": "oauth_access_token_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_access_token_session_id_idx": { + "name": "oauth_access_token_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_access_token_refresh_id_idx": { + "name": "oauth_access_token_refresh_id_idx", + "columns": [ + { + "expression": "refresh_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_access_token_user_client_idx": { + "name": "oauth_access_token_user_client_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_access_token_expires_at_idx": { + "name": "oauth_access_token_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_access_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_access_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_client", + "columnsFrom": ["client_id"], + "columnsTo": ["client_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_session_id_session_id_fk": { + "name": "oauth_access_token_session_id_session_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_access_token_user_id_user_id_fk": { + "name": "oauth_access_token_user_id_user_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_token_refresh_id_oauth_refresh_token_id_fk": { + "name": "oauth_access_token_refresh_id_oauth_refresh_token_id_fk", + "tableFrom": "oauth_access_token", + "tableTo": "oauth_refresh_token", + "columnsFrom": ["refresh_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_access_token_token_unique": { + "name": "oauth_access_token_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": { + "oauth_access_token_search_resource_check": { + "name": "oauth_access_token_search_resource_check", + "value": "NOT ('search:read' = ANY(\"oauth_access_token\".\"scopes\")) OR \"oauth_access_token\".\"resource\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.oauth_client": { + "name": "oauth_client", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "skip_consent": { + "name": "skip_consent", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "enable_end_session": { + "name": "enable_end_session", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contacts": { + "name": "contacts", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tos": { + "name": "tos", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy": { + "name": "policy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_statement": { + "name": "software_statement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "post_logout_redirect_uris": { + "name": "post_logout_redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public": { + "name": "public", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "require_pkce": { + "name": "require_pkce", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "oauth_client_user_id_idx": { + "name": "oauth_client_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_client_user_id_user_id_fk": { + "name": "oauth_client_user_id_user_id_fk", + "tableFrom": "oauth_client", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_client_client_id_unique": { + "name": "oauth_client_client_id_unique", + "nullsNotDistinct": false, + "columns": ["client_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_consent": { + "name": "oauth_consent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oauth_consent_client_id_idx": { + "name": "oauth_consent_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_consent_client_id_oauth_client_client_id_fk": { + "name": "oauth_consent_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "oauth_client", + "columnsFrom": ["client_id"], + "columnsTo": ["client_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_consent_user_id_user_id_fk": { + "name": "oauth_consent_user_id_user_id_fk", + "tableFrom": "oauth_consent", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_consent_user_client_reference_unique": { + "name": "oauth_consent_user_client_reference_unique", + "nullsNotDistinct": true, + "columns": ["user_id", "client_id", "reference_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_refresh_token": { + "name": "oauth_refresh_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "revoked": { + "name": "revoked", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "auth_time": { + "name": "auth_time", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "resource": { + "name": "resource", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "family_id": { + "name": "family_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oauth_refresh_token_client_id_idx": { + "name": "oauth_refresh_token_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_refresh_token_session_id_idx": { + "name": "oauth_refresh_token_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_refresh_token_user_client_idx": { + "name": "oauth_refresh_token_user_client_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_refresh_token_expires_at_idx": { + "name": "oauth_refresh_token_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_refresh_token_client_id_oauth_client_client_id_fk": { + "name": "oauth_refresh_token_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "oauth_client", + "columnsFrom": ["client_id"], + "columnsTo": ["client_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_refresh_token_session_id_session_id_fk": { + "name": "oauth_refresh_token_session_id_session_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_refresh_token_user_id_user_id_fk": { + "name": "oauth_refresh_token_user_id_user_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_refresh_token_family_id_oauth_token_family_id_fk": { + "name": "oauth_refresh_token_family_id_oauth_token_family_id_fk", + "tableFrom": "oauth_refresh_token", + "tableTo": "oauth_token_family", + "columnsFrom": ["family_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_refresh_token_token_unique": { + "name": "oauth_refresh_token_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + }, + "oauth_refresh_token_family_generation_unique": { + "name": "oauth_refresh_token_family_generation_unique", + "nullsNotDistinct": false, + "columns": ["family_id", "generation"] + } + }, + "policies": {}, + "checkConstraints": { + "oauth_refresh_token_generation_check": { + "name": "oauth_refresh_token_generation_check", + "value": "\"oauth_refresh_token\".\"generation\" BETWEEN 0 AND 1000" + }, + "oauth_refresh_token_search_resource_check": { + "name": "oauth_refresh_token_search_resource_check", + "value": "NOT ('search:read' = ANY(\"oauth_refresh_token\".\"scopes\")) OR \"oauth_refresh_token\".\"resource\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.oauth_token_family": { + "name": "oauth_token_family", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consent_id": { + "name": "consent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "current_generation": { + "name": "current_generation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "oauth_token_family_client_id_idx": { + "name": "oauth_token_family_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_token_family_session_id_idx": { + "name": "oauth_token_family_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_token_family_user_client_idx": { + "name": "oauth_token_family_user_client_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_token_family_consent_id_idx": { + "name": "oauth_token_family_consent_id_idx", + "columns": [ + { + "expression": "consent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_token_family_expires_at_idx": { + "name": "oauth_token_family_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_token_family_client_id_oauth_client_client_id_fk": { + "name": "oauth_token_family_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_token_family", + "tableTo": "oauth_client", + "columnsFrom": ["client_id"], + "columnsTo": ["client_id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_token_family_session_id_session_id_fk": { + "name": "oauth_token_family_session_id_session_id_fk", + "tableFrom": "oauth_token_family", + "tableTo": "session", + "columnsFrom": ["session_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauth_token_family_user_id_user_id_fk": { + "name": "oauth_token_family_user_id_user_id_fk", + "tableFrom": "oauth_token_family", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_token_family_consent_id_oauth_consent_id_fk": { + "name": "oauth_token_family_consent_id_oauth_consent_id_fk", + "tableFrom": "oauth_token_family", + "tableTo": "oauth_consent", + "columnsFrom": ["consent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "oauth_token_family_generation_check": { + "name": "oauth_token_family_generation_check", + "value": "\"oauth_token_family\".\"current_generation\" BETWEEN 0 AND 1000" + } + }, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "session_policy_settings": { + "name": "session_policy_settings", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "security_policy_version": { + "name": "security_policy_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "require_sso": { + "name": "require_sso", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "whitelabel_settings": { + "name": "whitelabel_settings", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "data_retention_settings": { + "name": "data_retention_settings", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "org_usage_limit": { + "name": "org_usage_limit", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "storage_used_bytes": { + "name": "storage_used_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "limit_notifications": { + "name": "limit_notifications", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "credit_balance": { + "name": "credit_balance", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_access_request_settings": { + "name": "organization_access_request_settings", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "allow_requests": { + "name": "allow_requests", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "organization_access_request_settings_organization_id_organization_id_fk": { + "name": "organization_access_request_settings_organization_id_organization_id_fk", + "tableFrom": "organization_access_request_settings", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_access_request_settings_updated_by_user_id_fk": { + "name": "organization_access_request_settings_updated_by_user_id_fk", + "tableFrom": "organization_access_request_settings", + "tableTo": "user", + "columnsFrom": ["updated_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_byok_keys": { + "name": "organization_byok_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_byok_organization_provider_idx": { + "name": "organization_byok_organization_provider_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_byok_keys_organization_id_organization_id_fk": { + "name": "organization_byok_keys_organization_id_organization_id_fk", + "tableFrom": "organization_byok_keys", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_byok_keys_created_by_user_id_fk": { + "name": "organization_byok_keys_created_by_user_id_fk", + "tableFrom": "organization_byok_keys", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_member_usage_limit": { + "name": "organization_member_usage_limit", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usage_limit": { + "name": "usage_limit", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "set_by": { + "name": "set_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "org_member_usage_limit_org_user_unique": { + "name": "org_member_usage_limit_org_user_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "org_member_usage_limit_organization_id_idx": { + "name": "org_member_usage_limit_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_member_usage_limit_organization_id_organization_id_fk": { + "name": "organization_member_usage_limit_organization_id_organization_id_fk", + "tableFrom": "organization_member_usage_limit", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_member_usage_limit_user_id_user_id_fk": { + "name": "organization_member_usage_limit_user_id_user_id_fk", + "tableFrom": "organization_member_usage_limit", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_member_usage_limit_set_by_user_id_fk": { + "name": "organization_member_usage_limit_set_by_user_id_fk", + "tableFrom": "organization_member_usage_limit", + "tableTo": "user", + "columnsFrom": ["set_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_search_history": { + "name": "organization_search_history", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sources": { + "name": "sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "queries": { + "name": "queries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + } + }, + "indexes": { + "organization_search_history_user_idx": { + "name": "organization_search_history_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_search_history_organization_id_organization_id_fk": { + "name": "organization_search_history_organization_id_organization_id_fk", + "tableFrom": "organization_search_history", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_search_history_user_id_user_id_fk": { + "name": "organization_search_history_user_id_user_id_fk", + "tableFrom": "organization_search_history", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "organization_search_history_organization_id_user_id_pk": { + "name": "organization_search_history_organization_id_user_id_pk", + "columns": ["organization_id", "user_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_search_integration": { + "name": "organization_search_integration", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_type": { + "name": "connector_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approved": { + "name": "approved", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "organization_search_integration_organization_id_organization_id_fk": { + "name": "organization_search_integration_organization_id_organization_id_fk", + "tableFrom": "organization_search_integration", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "organization_search_integration_organization_id_connector_type_pk": { + "name": "organization_search_integration_organization_id_connector_type_pk", + "columns": ["organization_id", "connector_type"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_search_invocation": { + "name": "organization_search_invocation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "surface": { + "name": "surface", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_types": { + "name": "source_types", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "result_count": { + "name": "result_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_search_invocation_org_created_idx": { + "name": "organization_search_invocation_org_created_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_search_invocation_user_idx": { + "name": "organization_search_invocation_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_search_invocation_organization_id_organization_id_fk": { + "name": "organization_search_invocation_organization_id_organization_id_fk", + "tableFrom": "organization_search_invocation", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_search_invocation_user_id_user_id_fk": { + "name": "organization_search_invocation_user_id_user_id_fk", + "tableFrom": "organization_search_invocation", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organization_search_invocation_result_count_bounds": { + "name": "organization_search_invocation_result_count_bounds", + "value": "\"organization_search_invocation\".\"result_count\" BETWEEN 0 AND 100" + }, + "organization_search_invocation_source_types_bounds": { + "name": "organization_search_invocation_source_types_bounds", + "value": "cardinality(\"organization_search_invocation\".\"source_types\") <= 100" + } + }, + "isRLSEnabled": false + }, + "public.organization_search_mcp_invocation": { + "name": "organization_search_mcp_invocation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auth_kind": { + "name": "auth_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_search_mcp_invocation_org_created_idx": { + "name": "organization_search_mcp_invocation_org_created_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_search_mcp_invocation_user_idx": { + "name": "organization_search_mcp_invocation_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "org_search_mcp_invocation_org_fk": { + "name": "org_search_mcp_invocation_org_fk", + "tableFrom": "organization_search_mcp_invocation", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "org_search_mcp_invocation_user_fk": { + "name": "org_search_mcp_invocation_user_fk", + "tableFrom": "organization_search_mcp_invocation", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organization_search_mcp_invocation_tool_check": { + "name": "organization_search_mcp_invocation_tool_check", + "value": "\"organization_search_mcp_invocation\".\"tool_name\" IN ('search', 'read_document', 'chat')" + }, + "organization_search_mcp_invocation_outcome_check": { + "name": "organization_search_mcp_invocation_outcome_check", + "value": "\"organization_search_mcp_invocation\".\"outcome\" IN ('success', 'error', 'cancelled', 'rate_limited')" + }, + "organization_search_mcp_invocation_duration_check": { + "name": "organization_search_mcp_invocation_duration_check", + "value": "\"organization_search_mcp_invocation\".\"duration_ms\" >= 0" + }, + "organization_search_mcp_invocation_client_name_check": { + "name": "organization_search_mcp_invocation_client_name_check", + "value": "length(\"organization_search_mcp_invocation\".\"client_name\") <= 256" + }, + "organization_search_mcp_invocation_auth_check": { + "name": "organization_search_mcp_invocation_auth_check", + "value": "(\"organization_search_mcp_invocation\".\"auth_kind\" = 'oauth_access_token' AND \"organization_search_mcp_invocation\".\"oauth_client_id\" IS NOT NULL)\n OR (\"organization_search_mcp_invocation\".\"auth_kind\" IN ('personal_api_key', 'workspace_api_key') AND \"organization_search_mcp_invocation\".\"oauth_client_id\" IS NULL AND \"organization_search_mcp_invocation\".\"client_name\" IS NULL)" + } + }, + "isRLSEnabled": false + }, + "public.organization_secret": { + "name": "organization_secret", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_value": { + "name": "encrypted_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_secret_shared_unique": { + "name": "organization_secret_shared_unique", + "columns": [ + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"organization_secret\".\"owner_user_id\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_secret_member_unique": { + "name": "organization_secret_member_unique", + "columns": [ + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"organization_secret\".\"owner_user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "organization_secret_owner_idx": { + "name": "organization_secret_owner_idx", + "columns": [ + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_secret_source_id_organization_secret_source_id_fk": { + "name": "organization_secret_source_id_organization_secret_source_id_fk", + "tableFrom": "organization_secret", + "tableTo": "organization_secret_source", + "columnsFrom": ["source_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_secret_owner_user_id_user_id_fk": { + "name": "organization_secret_owner_user_id_user_id_fk", + "tableFrom": "organization_secret", + "tableTo": "user", + "columnsFrom": ["owner_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_secret_source": { + "name": "organization_secret_source", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organization_secret_source_org_unique": { + "name": "organization_secret_source_org_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "organization_secret_source_organization_id_organization_id_fk": { + "name": "organization_secret_source_organization_id_organization_id_fk", + "tableFrom": "organization_secret_source", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organization_secret_source_mode_check": { + "name": "organization_secret_source_mode_check", + "value": "\"organization_secret_source\".\"mode\" IN ('organization', 'member')" + } + }, + "isRLSEnabled": false + }, + "public.outbox_event": { + "name": "outbox_event", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_attempts": { + "name": "max_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10 + }, + "available_at": { + "name": "available_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "outbox_event_status_available_idx": { + "name": "outbox_event_status_available_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbox_event_pending_type_available_idx": { + "name": "outbox_event_pending_type_available_idx", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"outbox_event\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbox_event_locked_at_idx": { + "name": "outbox_event_locked_at_idx", + "columns": [ + { + "expression": "locked_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbox_event_type_created_idx": { + "name": "outbox_event_type_created_idx", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.paused_executions": { + "name": "paused_executions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_snapshot": { + "name": "execution_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "pause_points": { + "name": "pause_points", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "total_pause_count": { + "name": "total_pause_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "resumed_count": { + "name": "resumed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "automatic_resume_retry_count": { + "name": "automatic_resume_retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'paused'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "paused_at": { + "name": "paused_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "next_resume_at": { + "name": "next_resume_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "paused_executions_workflow_id_idx": { + "name": "paused_executions_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "paused_executions_status_idx": { + "name": "paused_executions_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "paused_executions_execution_id_unique": { + "name": "paused_executions_execution_id_unique", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "paused_executions_next_resume_at_idx": { + "name": "paused_executions_next_resume_at_idx", + "columns": [ + { + "expression": "next_resume_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "status = 'paused' AND next_resume_at IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "paused_executions_workflow_id_workflow_id_fk": { + "name": "paused_executions_workflow_id_workflow_id_fk", + "tableFrom": "paused_executions", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_credential_draft": { + "name": "pending_credential_draft", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_config": { + "name": "oauth_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pending_draft_organization_id_idx": { + "name": "pending_draft_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pending_draft_user_provider_org": { + "name": "pending_draft_user_provider_org", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pending_draft_user_provider_ws": { + "name": "pending_draft_user_provider_ws", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pending_credential_draft_user_id_user_id_fk": { + "name": "pending_credential_draft_user_id_user_id_fk", + "tableFrom": "pending_credential_draft", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pending_credential_draft_workspace_id_workspace_id_fk": { + "name": "pending_credential_draft_workspace_id_workspace_id_fk", + "tableFrom": "pending_credential_draft", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pending_credential_draft_organization_id_organization_id_fk": { + "name": "pending_credential_draft_organization_id_organization_id_fk", + "tableFrom": "pending_credential_draft", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pending_credential_draft_credential_id_credential_id_fk": { + "name": "pending_credential_draft_credential_id_credential_id_fk", + "tableFrom": "pending_credential_draft", + "tableTo": "credential", + "columnsFrom": ["credential_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "pending_draft_owner_check": { + "name": "pending_draft_owner_check", + "value": "num_nonnulls(\"pending_credential_draft\".\"workspace_id\", \"pending_credential_draft\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.permission_access_request": { + "name": "permission_access_request", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requester_id": { + "name": "requester_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope_key": { + "name": "scope_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_key": { + "name": "target_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target": { + "name": "target", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "target_label": { + "name": "target_label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "membership_id": { + "name": "membership_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "decision_reason": { + "name": "decision_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decided_by": { + "name": "decided_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "decision": { + "name": "decision", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "decided_at": { + "name": "decided_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "permission_access_request_pending_unique": { + "name": "permission_access_request_pending_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requester_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"permission_access_request\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_access_request_org_queue_idx": { + "name": "permission_access_request_org_queue_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_access_request_requester_idx": { + "name": "permission_access_request_requester_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requester_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permission_access_request_organization_id_organization_id_fk": { + "name": "permission_access_request_organization_id_organization_id_fk", + "tableFrom": "permission_access_request", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_access_request_requester_id_user_id_fk": { + "name": "permission_access_request_requester_id_user_id_fk", + "tableFrom": "permission_access_request", + "tableTo": "user", + "columnsFrom": ["requester_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_access_request_decided_by_user_id_fk": { + "name": "permission_access_request_decided_by_user_id_fk", + "tableFrom": "permission_access_request", + "tableTo": "user", + "columnsFrom": ["decided_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "permission_access_request_status_check": { + "name": "permission_access_request_status_check", + "value": "\"permission_access_request\".\"status\" in ('pending', 'fulfilled', 'declined', 'cancelled', 'closed')" + } + }, + "isRLSEnabled": false + }, + "public.permission_group": { + "name": "permission_group", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "membership_mode": { + "name": "membership_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'inherit'" + } + }, + "indexes": { + "permission_group_created_by_idx": { + "name": "permission_group_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_group_organization_name_unique": { + "name": "permission_group_organization_name_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_group_organization_default_unique": { + "name": "permission_group_organization_default_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "is_default = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permission_group_organization_id_organization_id_fk": { + "name": "permission_group_organization_id_organization_id_fk", + "tableFrom": "permission_group", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_created_by_user_id_fk": { + "name": "permission_group_created_by_user_id_fk", + "tableFrom": "permission_group", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permission_group_member": { + "name": "permission_group_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "permission_group_id": { + "name": "permission_group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assigned_by": { + "name": "assigned_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "permission_group_member_group_id_idx": { + "name": "permission_group_member_group_id_idx", + "columns": [ + { + "expression": "permission_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_group_member_group_user_unique": { + "name": "permission_group_member_group_user_unique", + "columns": [ + { + "expression": "permission_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_group_member_organization_user_idx": { + "name": "permission_group_member_organization_user_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permission_group_member_permission_group_id_permission_group_id_fk": { + "name": "permission_group_member_permission_group_id_permission_group_id_fk", + "tableFrom": "permission_group_member", + "tableTo": "permission_group", + "columnsFrom": ["permission_group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_member_organization_id_organization_id_fk": { + "name": "permission_group_member_organization_id_organization_id_fk", + "tableFrom": "permission_group_member", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_member_user_id_user_id_fk": { + "name": "permission_group_member_user_id_user_id_fk", + "tableFrom": "permission_group_member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_member_assigned_by_user_id_fk": { + "name": "permission_group_member_assigned_by_user_id_fk", + "tableFrom": "permission_group_member", + "tableTo": "user", + "columnsFrom": ["assigned_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permission_group_workspace": { + "name": "permission_group_workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "permission_group_id": { + "name": "permission_group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "permission_group_workspace_workspace_id_idx": { + "name": "permission_group_workspace_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permission_group_workspace_group_workspace_unique": { + "name": "permission_group_workspace_group_workspace_unique", + "columns": [ + { + "expression": "permission_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permission_group_workspace_permission_group_id_permission_group_id_fk": { + "name": "permission_group_workspace_permission_group_id_permission_group_id_fk", + "tableFrom": "permission_group_workspace", + "tableTo": "permission_group", + "columnsFrom": ["permission_group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_workspace_workspace_id_workspace_id_fk": { + "name": "permission_group_workspace_workspace_id_workspace_id_fk", + "tableFrom": "permission_group_workspace", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "permission_group_workspace_organization_id_organization_id_fk": { + "name": "permission_group_workspace_organization_id_organization_id_fk", + "tableFrom": "permission_group_workspace", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permissions": { + "name": "permissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_type": { + "name": "permission_type", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "permissions_user_id_idx": { + "name": "permissions_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_entity_idx": { + "name": "permissions_entity_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_user_entity_type_idx": { + "name": "permissions_user_entity_type_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_user_entity_permission_idx": { + "name": "permissions_user_entity_permission_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_unique_constraint": { + "name": "permissions_unique_constraint", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permissions_user_id_user_id_fk": { + "name": "permissions_user_id_user_id_fk", + "tableFrom": "permissions", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pinned_item": { + "name": "pinned_item", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pinned_at": { + "name": "pinned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pinned_item_user_workspace_idx": { + "name": "pinned_item_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pinned_item_resource_idx": { + "name": "pinned_item_resource_idx", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pinned_item_user_resource_unique": { + "name": "pinned_item_user_resource_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pinned_item_user_id_user_id_fk": { + "name": "pinned_item_user_id_user_id_fk", + "tableFrom": "pinned_item", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pinned_item_workspace_id_workspace_id_fk": { + "name": "pinned_item_workspace_id_workspace_id_fk", + "tableFrom": "pinned_item", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "project_organization_archive_id_idx": { + "name": "project_organization_archive_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "project_owner_archive_id_idx": { + "name": "project_owner_archive_id_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "project_organization_id_organization_id_fk": { + "name": "project_organization_id_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "project_owner_id_user_id_fk": { + "name": "project_owner_id_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "project_name_length": { + "name": "project_name_length", + "value": "char_length(btrim(\"project\".\"name\")) BETWEEN 1 AND 100" + } + }, + "isRLSEnabled": false + }, + "public.project_workspace": { + "name": "project_workspace", + "schema": "", + "columns": { + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "project_workspace_workspace_id_unique": { + "name": "project_workspace_workspace_id_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "project_workspace_project_id_project_id_fk": { + "name": "project_workspace_project_id_project_id_fk", + "tableFrom": "project_workspace", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "project_workspace_workspace_id_workspace_id_fk": { + "name": "project_workspace_workspace_id_workspace_id_fk", + "tableFrom": "project_workspace", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "project_workspace_project_id_workspace_id_pk": { + "name": "project_workspace_project_id_workspace_id_pk", + "columns": ["project_id", "workspace_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.public_share": { + "name": "public_share", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'public'" + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_emails": { + "name": "allowed_emails", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "public_share_token_unique": { + "name": "public_share_token_unique", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "public_share_resource_unique": { + "name": "public_share_resource_unique", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "public_share_resource_id_idx": { + "name": "public_share_resource_id_idx", + "columns": [ + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "public_share_workspace_id_idx": { + "name": "public_share_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "public_share_workspace_id_workspace_id_fk": { + "name": "public_share_workspace_id_workspace_id_fk", + "tableFrom": "public_share", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "public_share_created_by_user_id_fk": { + "name": "public_share_created_by_user_id_fk", + "tableFrom": "public_share", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rate_limit_bucket": { + "name": "rate_limit_bucket", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "tokens": { + "name": "tokens", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "blocked_until": { + "name": "blocked_until", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "capacity_state": { + "name": "capacity_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.resource_policy": { + "name": "resource_policy", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "document": { + "name": "document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "resource_policy_organization_id_idx": { + "name": "resource_policy_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "resource_policy_resource_unique": { + "name": "resource_policy_resource_unique", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "resource_policy_workspace_id_idx": { + "name": "resource_policy_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_policy_workspace_id_workspace_id_fk": { + "name": "resource_policy_workspace_id_workspace_id_fk", + "tableFrom": "resource_policy", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "resource_policy_organization_id_organization_id_fk": { + "name": "resource_policy_organization_id_organization_id_fk", + "tableFrom": "resource_policy", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "resource_policy_created_by_user_id_fk": { + "name": "resource_policy_created_by_user_id_fk", + "tableFrom": "resource_policy", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "resource_policy_updated_by_user_id_fk": { + "name": "resource_policy_updated_by_user_id_fk", + "tableFrom": "resource_policy", + "tableTo": "user", + "columnsFrom": ["updated_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "resource_policy_owner_check": { + "name": "resource_policy_owner_check", + "value": "num_nonnulls(\"resource_policy\".\"workspace_id\", \"resource_policy\".\"organization_id\") = 1" + } + }, + "isRLSEnabled": false + }, + "public.resume_queue": { + "name": "resume_queue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "paused_execution_id": { + "name": "paused_execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_execution_id": { + "name": "parent_execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "new_execution_id": { + "name": "new_execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "context_id": { + "name": "context_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resume_input": { + "name": "resume_input", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "queued_at": { + "name": "queued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "resume_queue_parent_status_idx": { + "name": "resume_queue_parent_status_idx", + "columns": [ + { + "expression": "parent_execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "resume_queue_new_execution_idx": { + "name": "resume_queue_new_execution_idx", + "columns": [ + { + "expression": "new_execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resume_queue_paused_execution_id_paused_executions_id_fk": { + "name": "resume_queue_paused_execution_id_paused_executions_id_fk", + "tableFrom": "resume_queue", + "tableTo": "paused_executions", + "columnsFrom": ["paused_execution_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sandbox_image": { + "name": "sandbox_image", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "spec_hash": { + "name": "spec_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "spec": { + "name": "spec", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "sandbox_image_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "image_ref": { + "name": "image_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_image_id": { + "name": "provider_image_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "build_id": { + "name": "build_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "materialization_generation": { + "name": "materialization_generation", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_detail": { + "name": "error_detail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sandbox_image_provider_spec_unique": { + "name": "sandbox_image_provider_spec_unique", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "spec_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sandbox_image_status_idx": { + "name": "sandbox_image_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sandbox_image_last_used_idx": { + "name": "sandbox_image_last_used_idx", + "columns": [ + { + "expression": "last_used_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_connection": { + "name": "scim_connection", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_request_at": { + "name": "last_request_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "reconcile_lock_token": { + "name": "reconcile_lock_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reconcile_lease_at": { + "name": "reconcile_lease_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "reconciled_at": { + "name": "reconciled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_connection_organization_unique": { + "name": "scim_connection_organization_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_connection_reconcile_due_idx": { + "name": "scim_connection_reconcile_due_idx", + "columns": [ + { + "expression": "reconciled_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_connection_organization_id_organization_id_fk": { + "name": "scim_connection_organization_id_organization_id_fk", + "tableFrom": "scim_connection", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_connection_created_by_user_id_fk": { + "name": "scim_connection_created_by_user_id_fk", + "tableFrom": "scim_connection", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_credential": { + "name": "scim_credential", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_prefix": { + "name": "token_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_by": { + "name": "revoked_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_credential_token_hash_unique": { + "name": "scim_credential_token_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_credential_connection_idx": { + "name": "scim_credential_connection_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_credential_connection_id_scim_connection_id_fk": { + "name": "scim_credential_connection_id_scim_connection_id_fk", + "tableFrom": "scim_credential", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_credential_revoked_by_user_id_fk": { + "name": "scim_credential_revoked_by_user_id_fk", + "tableFrom": "scim_credential", + "tableTo": "user", + "columnsFrom": ["revoked_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "scim_credential_created_by_user_id_fk": { + "name": "scim_credential_created_by_user_id_fk", + "tableFrom": "scim_credential", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_group": { + "name": "scim_group", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name_key": { + "name": "display_name_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "order_key": { + "name": "order_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_group_connection_display_name_unique": { + "name": "scim_group_connection_display_name_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_name_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_connection_external_id_unique": { + "name": "scim_group_connection_external_id_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "external_id is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_connection_order_idx": { + "name": "scim_group_connection_order_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "order_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_group_connection_id_scim_connection_id_fk": { + "name": "scim_group_connection_id_scim_connection_id_fk", + "tableFrom": "scim_group", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_group_mapping": { + "name": "scim_group_mapping", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_kind": { + "name": "target_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_group_id": { + "name": "permission_group_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permission_type": { + "name": "permission_type", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_group_mapping_group_idx": { + "name": "scim_group_mapping_group_idx", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_mapping_permission_group_idx": { + "name": "scim_group_mapping_permission_group_idx", + "columns": [ + { + "expression": "permission_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_mapping_workspace_idx": { + "name": "scim_group_mapping_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_mapping_group_target_unique": { + "name": "scim_group_mapping_group_target_unique", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"permission_group_id\", \"workspace_id\", \"role\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_group_mapping_group_id_scim_group_id_fk": { + "name": "scim_group_mapping_group_id_scim_group_id_fk", + "tableFrom": "scim_group_mapping", + "tableTo": "scim_group", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_group_mapping_permission_group_id_permission_group_id_fk": { + "name": "scim_group_mapping_permission_group_id_permission_group_id_fk", + "tableFrom": "scim_group_mapping", + "tableTo": "permission_group", + "columnsFrom": ["permission_group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_group_mapping_workspace_id_workspace_id_fk": { + "name": "scim_group_mapping_workspace_id_workspace_id_fk", + "tableFrom": "scim_group_mapping", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_group_mapping_created_by_user_id_fk": { + "name": "scim_group_mapping_created_by_user_id_fk", + "tableFrom": "scim_group_mapping", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "scim_group_mapping_target_shape": { + "name": "scim_group_mapping_target_shape", + "value": "(\n (\"scim_group_mapping\".\"target_kind\" = 'permission_group' AND \"scim_group_mapping\".\"permission_group_id\" IS NOT NULL AND \"scim_group_mapping\".\"workspace_id\" IS NULL AND \"scim_group_mapping\".\"permission_type\" IS NULL AND \"scim_group_mapping\".\"role\" IS NULL)\n OR (\"scim_group_mapping\".\"target_kind\" = 'workspace' AND \"scim_group_mapping\".\"workspace_id\" IS NOT NULL AND \"scim_group_mapping\".\"permission_type\" IS NOT NULL AND \"scim_group_mapping\".\"permission_group_id\" IS NULL AND \"scim_group_mapping\".\"role\" IS NULL)\n OR (\"scim_group_mapping\".\"target_kind\" = 'org_role' AND \"scim_group_mapping\".\"role\" IS NOT NULL AND \"scim_group_mapping\".\"permission_group_id\" IS NULL AND \"scim_group_mapping\".\"workspace_id\" IS NULL AND \"scim_group_mapping\".\"permission_type\" IS NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.scim_group_member": { + "name": "scim_group_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scim_user_id": { + "name": "scim_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_group_member_group_user_unique": { + "name": "scim_group_member_group_user_unique", + "columns": [ + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scim_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_group_member_scim_user_idx": { + "name": "scim_group_member_scim_user_idx", + "columns": [ + { + "expression": "scim_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_group_member_group_id_scim_group_id_fk": { + "name": "scim_group_member_group_id_scim_group_id_fk", + "tableFrom": "scim_group_member", + "tableTo": "scim_group", + "columnsFrom": ["group_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_group_member_scim_user_id_scim_user_id_fk": { + "name": "scim_group_member_scim_user_id_scim_user_id_fk", + "tableFrom": "scim_group_member", + "tableTo": "scim_user", + "columnsFrom": ["scim_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_projection_grant": { + "name": "scim_projection_grant", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scim_user_id": { + "name": "scim_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_kind": { + "name": "target_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_type": { + "name": "permission_type", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "baseline_permission": { + "name": "baseline_permission", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'directory'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_projection_grant_user_target_unique": { + "name": "scim_projection_grant_user_target_unique", + "columns": [ + { + "expression": "scim_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_projection_grant_connection_idx": { + "name": "scim_projection_grant_connection_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_projection_grant_connection_id_scim_connection_id_fk": { + "name": "scim_projection_grant_connection_id_scim_connection_id_fk", + "tableFrom": "scim_projection_grant", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_projection_grant_scim_user_id_scim_user_id_fk": { + "name": "scim_projection_grant_scim_user_id_scim_user_id_fk", + "tableFrom": "scim_projection_grant", + "tableTo": "scim_user", + "columnsFrom": ["scim_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_request_log": { + "name": "scim_request_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scim_type": { + "name": "scim_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_request_log_connection_created_idx": { + "name": "scim_request_log_connection_created_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_request_log_connection_id_scim_connection_id_fk": { + "name": "scim_request_log_connection_id_scim_connection_id_fk", + "tableFrom": "scim_request_log", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_user": { + "name": "scim_user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "attributes": { + "name": "attributes", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "order_key": { + "name": "order_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_user_connection_user_unique": { + "name": "scim_user_connection_user_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_user_connection_user_name_unique": { + "name": "scim_user_connection_user_name_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_user_connection_external_id_unique": { + "name": "scim_user_connection_external_id_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "external_id is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_user_connection_order_idx": { + "name": "scim_user_connection_order_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "order_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_user_user_idx": { + "name": "scim_user_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_user_connection_id_scim_connection_id_fk": { + "name": "scim_user_connection_id_scim_connection_id_fk", + "tableFrom": "scim_user", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_user_user_id_user_id_fk": { + "name": "scim_user_user_id_user_id_fk", + "tableFrom": "scim_user", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scim_user_tombstone": { + "name": "scim_user_tombstone", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scim_user_tombstone_connection_external_id_unique": { + "name": "scim_user_tombstone_connection_external_id_unique", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scim_user_tombstone_user_idx": { + "name": "scim_user_tombstone_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scim_user_tombstone_connection_id_scim_connection_id_fk": { + "name": "scim_user_tombstone_connection_id_scim_connection_id_fk", + "tableFrom": "scim_user_tombstone", + "tableTo": "scim_connection", + "columnsFrom": ["connection_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scim_user_tombstone_user_id_user_id_fk": { + "name": "scim_user_tombstone_user_id_user_id_fk", + "tableFrom": "scim_user_tombstone", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.secret_usage": { + "name": "secret_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_name": { + "name": "secret_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_scope": { + "name": "secret_scope", + "type": "secret_usage_scope", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "secret_owner_user_id": { + "name": "secret_owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "source": { + "name": "source", + "type": "secret_usage_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "usage_date": { + "name": "usage_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "use_count": { + "name": "use_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "last_execution_id": { + "name": "last_execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_trigger": { + "name": "last_trigger", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "secret_usage_bucket_unique": { + "name": "secret_usage_bucket_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_scope", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "usage_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "secret_usage_secret_recent_idx": { + "name": "secret_usage_secret_recent_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_scope", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_used_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "secret_usage_workspace_id_workspace_id_fk": { + "name": "secret_usage_workspace_id_workspace_id_fk", + "tableFrom": "secret_usage", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "session_user_id_idx": { + "name": "session_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "session_active_organization_id_organization_id_fk": { + "name": "session_active_organization_id_organization_id_fk", + "tableFrom": "session", + "tableTo": "organization", + "columnsFrom": ["active_organization_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.settings": { + "name": "settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "auto_connect": { + "name": "auto_connect", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "telemetry_enabled": { + "name": "telemetry_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "email_preferences": { + "name": "email_preferences", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "billing_usage_notifications_enabled": { + "name": "billing_usage_notifications_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "show_training_controls": { + "name": "show_training_controls", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "super_user_mode_enabled": { + "name": "super_user_mode_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "mothership_environment": { + "name": "mothership_environment", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "error_notifications_enabled": { + "name": "error_notifications_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "snap_to_grid_size": { + "name": "snap_to_grid_size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "show_action_bar": { + "name": "show_action_bar", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auto_focus_on_click": { + "name": "auto_focus_on_click", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "copilot_enabled_models": { + "name": "copilot_enabled_models", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "copilot_auto_allowed_tools": { + "name": "copilot_auto_allowed_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "last_active_workspace_id": { + "name": "last_active_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "settings_user_id_user_id_fk": { + "name": "settings_user_id_user_id_fk", + "tableFrom": "settings", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "settings_user_id_unique": { + "name": "settings_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sim_trigger_state": { + "name": "sim_trigger_state", + "schema": "", + "columns": { + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_key": { + "name": "scope_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "last_fired_at": { + "name": "last_fired_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sim_trigger_state_workflow_id_workflow_id_fk": { + "name": "sim_trigger_state_workflow_id_workflow_id_fk", + "tableFrom": "sim_trigger_state", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "sim_trigger_state_workflow_id_block_id_scope_key_pk": { + "name": "sim_trigger_state_workflow_id_block_id_scope_key_pk", + "columns": ["workflow_id", "block_id", "scope_key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.skill": { + "name": "skill", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "skill_workspace_name_unique": { + "name": "skill_workspace_name_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "skill_workspace_id_workspace_id_fk": { + "name": "skill_workspace_id_workspace_id_fk", + "tableFrom": "skill", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "skill_user_id_user_id_fk": { + "name": "skill_user_id_user_id_fk", + "tableFrom": "skill", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.skill_member": { + "name": "skill_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "skill_id": { + "name": "skill_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invited_by": { + "name": "invited_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "skill_member_user_id_idx": { + "name": "skill_member_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "skill_member_unique": { + "name": "skill_member_unique", + "columns": [ + { + "expression": "skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "skill_member_skill_id_skill_id_fk": { + "name": "skill_member_skill_id_skill_id_fk", + "tableFrom": "skill_member", + "tableTo": "skill", + "columnsFrom": ["skill_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "skill_member_user_id_user_id_fk": { + "name": "skill_member_user_id_user_id_fk", + "tableFrom": "skill_member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "skill_member_invited_by_user_id_fk": { + "name": "skill_member_invited_by_user_id_fk", + "tableFrom": "skill_member", + "tableTo": "user", + "columnsFrom": ["invited_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_app": { + "name": "slack_app", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_client_secret": { + "name": "encrypted_client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_signing_secret": { + "name": "encrypted_signing_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revision": { + "name": "revision", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "slack_app_organization_id_organization_id_fk": { + "name": "slack_app_organization_id_organization_id_fk", + "tableFrom": "slack_app", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "slack_app_owner_check": { + "name": "slack_app_owner_check", + "value": "(\"slack_app\".\"kind\" = 'custom' AND \"slack_app\".\"organization_id\" IS NOT NULL) OR (\"slack_app\".\"kind\" = 'shared' AND \"slack_app\".\"organization_id\" IS NULL)" + }, + "slack_app_custom_credentials_check": { + "name": "slack_app_custom_credentials_check", + "value": "\"slack_app\".\"kind\" = 'shared' OR (\"slack_app\".\"client_id\" IS NOT NULL AND \"slack_app\".\"encrypted_client_secret\" IS NOT NULL AND \"slack_app\".\"encrypted_signing_secret\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.slack_search_installation": { + "name": "slack_search_installation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "app_id": { + "name": "app_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_app_id": { + "name": "slack_app_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team_name": { + "name": "team_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bot_user_id": { + "name": "bot_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enterprise_id": { + "name": "enterprise_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "credential_version": { + "name": "credential_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_outcome": { + "name": "last_outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_event_at": { + "name": "last_event_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "slack_search_installation_organization_idx": { + "name": "slack_search_installation_organization_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_installation_credential_unique": { + "name": "slack_search_installation_credential_unique", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_installation_app_team_unique": { + "name": "slack_search_installation_app_team_unique", + "columns": [ + { + "expression": "app_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_installation_active_team_unique": { + "name": "slack_search_installation_active_team_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"slack_search_installation\".\"enabled\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_search_installation_organization_id_organization_id_fk": { + "name": "slack_search_installation_organization_id_organization_id_fk", + "tableFrom": "slack_search_installation", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_search_installation_credential_id_credential_id_fk": { + "name": "slack_search_installation_credential_id_credential_id_fk", + "tableFrom": "slack_search_installation", + "tableTo": "credential", + "columnsFrom": ["credential_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_search_installation_slack_app_id_slack_app_id_fk": { + "name": "slack_search_installation_slack_app_id_slack_app_id_fk", + "tableFrom": "slack_search_installation", + "tableTo": "slack_app", + "columnsFrom": ["slack_app_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_search_turn": { + "name": "slack_search_turn", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ordinal": { + "name": "ordinal", + "type": "integer", + "primaryKey": false, + "notNull": true, + "identity": { + "type": "always", + "name": "slack_search_turn_ordinal_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "installation_id": { + "name": "installation_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conversation_key": { + "name": "conversation_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "lease_id": { + "name": "lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "slack_search_turn_event_unique": { + "name": "slack_search_turn_event_unique", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_turn_pending_idx": { + "name": "slack_search_turn_pending_idx", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_turn_thread_idx": { + "name": "slack_search_turn_thread_idx", + "columns": [ + { + "expression": "conversation_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "slack_search_turn_active_thread_unique": { + "name": "slack_search_turn_active_thread_unique", + "columns": [ + { + "expression": "conversation_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"slack_search_turn\".\"status\" = 'running'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_search_turn_installation_id_slack_search_installation_id_fk": { + "name": "slack_search_turn_installation_id_slack_search_installation_id_fk", + "tableFrom": "slack_search_turn", + "tableTo": "slack_search_installation", + "columnsFrom": ["installation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sso_domain": { + "name": "sso_domain", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "primary_provider_id": { + "name": "primary_provider_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sso_domain_organization_id_idx": { + "name": "sso_domain_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_domain_domain_idx": { + "name": "sso_domain_domain_idx", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_domain_org_domain_unique": { + "name": "sso_domain_org_domain_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_domain_verified_unique": { + "name": "sso_domain_verified_unique", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "status = 'verified'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sso_domain_organization_id_organization_id_fk": { + "name": "sso_domain_organization_id_organization_id_fk", + "tableFrom": "sso_domain", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "sso_domain_created_by_user_id_fk": { + "name": "sso_domain_created_by_user_id_fk", + "tableFrom": "sso_domain", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sso_provider": { + "name": "sso_provider", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oidc_config": { + "name": "oidc_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "saml_config": { + "name": "saml_config", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domain_verified": { + "name": "domain_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "jit_provisioning_enabled": { + "name": "jit_provisioning_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": { + "sso_provider_provider_id_unique": { + "name": "sso_provider_provider_id_unique", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_provider_domain_idx": { + "name": "sso_provider_domain_idx", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_provider_user_id_idx": { + "name": "sso_provider_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sso_provider_organization_id_idx": { + "name": "sso_provider_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sso_provider_user_id_user_id_fk": { + "name": "sso_provider_user_id_user_id_fk", + "tableFrom": "sso_provider", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "sso_provider_organization_id_organization_id_fk": { + "name": "sso_provider_organization_id_organization_id_fk", + "tableFrom": "sso_provider", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscription": { + "name": "subscription", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "period_start": { + "name": "period_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "period_end": { + "name": "period_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "cancel_at": { + "name": "cancel_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "seats": { + "name": "seats", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "trial_start": { + "name": "trial_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "trial_end": { + "name": "trial_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "billing_interval": { + "name": "billing_interval", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_schedule_id": { + "name": "stripe_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "last_closed_period_start": { + "name": "last_closed_period_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "subscription_reference_status_idx": { + "name": "subscription_reference_status_idx", + "columns": [ + { + "expression": "reference_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "subscription_cycle_close_lagging_idx": { + "name": "subscription_cycle_close_lagging_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"subscription\".\"status\" in ('active', 'past_due') and \"subscription\".\"period_start\" is not null and (\"subscription\".\"last_closed_period_start\" is null or \"subscription\".\"last_closed_period_start\" < \"subscription\".\"period_start\")", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "check_enterprise_metadata": { + "name": "check_enterprise_metadata", + "value": "plan != 'enterprise' OR metadata IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.table_jobs": { + "name": "table_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "rows_processed": { + "name": "rows_processed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "table_jobs_one_active_per_table": { + "name": "table_jobs_one_active_per_table", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"table_jobs\".\"status\" = 'running' AND \"table_jobs\".\"type\" <> 'export'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_jobs_watchdog_idx": { + "name": "table_jobs_watchdog_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_jobs_table_started_idx": { + "name": "table_jobs_table_started_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "table_jobs_table_id_user_table_definitions_id_fk": { + "name": "table_jobs_table_id_user_table_definitions_id_fk", + "tableFrom": "table_jobs", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_jobs_workspace_id_workspace_id_fk": { + "name": "table_jobs_workspace_id_workspace_id_fk", + "tableFrom": "table_jobs", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.table_row_executions": { + "name": "table_row_executions", + "schema": "", + "columns": { + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "row_id": { + "name": "row_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "group_id": { + "name": "group_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "job_id": { + "name": "job_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "running_block_ids": { + "name": "running_block_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "block_errors": { + "name": "block_errors", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "capability_governed_user_id": { + "name": "capability_governed_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enrichment_details": { + "name": "enrichment_details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "table_row_executions_table_status_idx": { + "name": "table_row_executions_table_status_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"table_row_executions\".\"status\" IN ('queued', 'running', 'pending')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_row_executions_execution_id_idx": { + "name": "table_row_executions_execution_id_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"table_row_executions\".\"execution_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_row_executions_table_group_idx": { + "name": "table_row_executions_table_group_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "table_row_executions_table_id_user_table_definitions_id_fk": { + "name": "table_row_executions_table_id_user_table_definitions_id_fk", + "tableFrom": "table_row_executions", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_row_executions_row_id_user_table_rows_id_fk": { + "name": "table_row_executions_row_id_user_table_rows_id_fk", + "tableFrom": "table_row_executions", + "tableTo": "user_table_rows", + "columnsFrom": ["row_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_row_executions_capability_governed_user_id_user_id_fk": { + "name": "table_row_executions_capability_governed_user_id_user_id_fk", + "tableFrom": "table_row_executions", + "tableTo": "user", + "columnsFrom": ["capability_governed_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "table_row_executions_row_id_group_id_pk": { + "name": "table_row_executions_row_id_group_id_pk", + "columns": ["row_id", "group_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.table_run_dispatches": { + "name": "table_run_dispatches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "cursor": { + "name": "cursor", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "limit": { + "name": "limit", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "processed_count": { + "name": "processed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_manual_run": { + "name": "is_manual_run", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "triggered_by_user_id": { + "name": "triggered_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "capability_governed_user_id": { + "name": "capability_governed_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "heartbeat_at": { + "name": "heartbeat_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "table_run_dispatches_active_idx": { + "name": "table_run_dispatches_active_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_run_dispatches_watchdog_idx": { + "name": "table_run_dispatches_watchdog_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requested_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_run_dispatches_governed_active_idx": { + "name": "table_run_dispatches_governed_active_idx", + "columns": [ + { + "expression": "capability_governed_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"table_run_dispatches\".\"status\" IN ('pending', 'dispatching')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "table_run_dispatches_table_id_user_table_definitions_id_fk": { + "name": "table_run_dispatches_table_id_user_table_definitions_id_fk", + "tableFrom": "table_run_dispatches", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_run_dispatches_workspace_id_workspace_id_fk": { + "name": "table_run_dispatches_workspace_id_workspace_id_fk", + "tableFrom": "table_run_dispatches", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_run_dispatches_triggered_by_user_id_user_id_fk": { + "name": "table_run_dispatches_triggered_by_user_id_user_id_fk", + "tableFrom": "table_run_dispatches", + "tableTo": "user", + "columnsFrom": ["triggered_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "table_run_dispatches_capability_governed_user_id_user_id_fk": { + "name": "table_run_dispatches_capability_governed_user_id_user_id_fk", + "tableFrom": "table_run_dispatches", + "tableTo": "user", + "columnsFrom": ["capability_governed_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.table_views": { + "name": "table_views", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "table_views_table_created_idx": { + "name": "table_views_table_created_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_views_workspace_created_idx": { + "name": "table_views_workspace_created_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "table_views_table_default_unique": { + "name": "table_views_table_default_unique", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "is_default = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "table_views_table_id_user_table_definitions_id_fk": { + "name": "table_views_table_id_user_table_definitions_id_fk", + "tableFrom": "table_views", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_views_workspace_id_workspace_id_fk": { + "name": "table_views_workspace_id_workspace_id_fk", + "tableFrom": "table_views", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "table_views_created_by_user_id_fk": { + "name": "table_views_created_by_user_id_fk", + "tableFrom": "table_views", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.upload_session": { + "name": "upload_session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purpose": { + "name": "purpose", + "type": "upload_session_purpose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "method": { + "name": "method", + "type": "upload_session_method", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "storage_context": { + "name": "storage_context", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "final_key": { + "name": "final_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_provider": { + "name": "storage_provider", + "type": "upload_session_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "provider_upload_id": { + "name": "provider_upload_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_object_version": { + "name": "provider_object_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "part_size": { + "name": "part_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "part_count": { + "name": "part_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "upload_session_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'uploading'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "processing_lease_id": { + "name": "processing_lease_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "processing_lease_expires_at": { + "name": "processing_lease_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_file_id": { + "name": "completed_file_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "upload_session_token_hash_unique": { + "name": "upload_session_token_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "upload_session_final_key_unique": { + "name": "upload_session_final_key_unique", + "columns": [ + { + "expression": "final_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "upload_session_status_expires_at_idx": { + "name": "upload_session_status_expires_at_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usage_log": { + "name": "usage_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "usage_log_category", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "usage_log_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "event_key": { + "name": "event_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "billing_entity_type": { + "name": "billing_entity_type", + "type": "billing_entity_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "billing_entity_id": { + "name": "billing_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "billing_period_start": { + "name": "billing_period_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "billing_period_end": { + "name": "billing_period_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "usage_log_user_created_at_idx": { + "name": "usage_log_user_created_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_source_idx": { + "name": "usage_log_source_idx", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_workflow_id_idx": { + "name": "usage_log_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_event_key_unique": { + "name": "usage_log_event_key_unique", + "columns": [ + { + "expression": "event_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"usage_log\".\"event_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_billing_entity_period_idx": { + "name": "usage_log_billing_entity_period_idx", + "columns": [ + { + "expression": "billing_entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_period_start", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_period_end", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_log\".\"billing_entity_type\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_billing_period_cost_idx": { + "name": "usage_log_billing_period_cost_idx", + "columns": [ + { + "expression": "billing_entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_period_start", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_period_end", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_log\".\"billing_entity_type\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_billing_entity_created_at_cost_idx": { + "name": "usage_log_billing_entity_created_at_cost_idx", + "columns": [ + { + "expression": "billing_entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "billing_entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_log\".\"billing_entity_type\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_workspace_created_at_idx": { + "name": "usage_log_workspace_created_at_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "usage_log_execution_id_idx": { + "name": "usage_log_execution_id_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "usage_log_user_id_user_id_fk": { + "name": "usage_log_user_id_user_id_fk", + "tableFrom": "usage_log", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "usage_log_workspace_id_workspace_id_fk": { + "name": "usage_log_workspace_id_workspace_id_fk", + "tableFrom": "usage_log", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "usage_log_workflow_id_workflow_id_fk": { + "name": "usage_log_workflow_id_workflow_id_fk", + "tableFrom": "usage_log", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "usage_log_billing_scope_all_or_none": { + "name": "usage_log_billing_scope_all_or_none", + "value": "(\n (\"usage_log\".\"billing_entity_type\" IS NULL AND \"usage_log\".\"billing_entity_id\" IS NULL AND \"usage_log\".\"billing_period_start\" IS NULL AND \"usage_log\".\"billing_period_end\" IS NULL)\n OR\n (\"usage_log\".\"billing_entity_type\" IS NOT NULL AND \"usage_log\".\"billing_entity_id\" IS NOT NULL AND \"usage_log\".\"billing_period_start\" IS NOT NULL AND \"usage_log\".\"billing_period_end\" IS NOT NULL AND \"usage_log\".\"billing_period_start\" < \"usage_log\".\"billing_period_end\")\n )" + } + }, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "normalized_email": { + "name": "normalized_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'user'" + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "suspended_at": { + "name": "suspended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "suspension_source": { + "name": "suspension_source", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_email_lower_idx": { + "name": "user_email_lower_idx", + "columns": [ + { + "expression": "lower(btrim(\"email\"))", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + }, + "user_normalized_email_unique": { + "name": "user_normalized_email_unique", + "nullsNotDistinct": false, + "columns": ["normalized_email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_stats": { + "name": "user_stats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "current_usage_limit": { + "name": "current_usage_limit", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": "'5'" + }, + "usage_limit_updated_at": { + "name": "usage_limit_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "last_period_cost": { + "name": "last_period_cost", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": "'0'" + }, + "billed_overage_this_period": { + "name": "billed_overage_this_period", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "credit_balance": { + "name": "credit_balance", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "last_period_copilot_cost": { + "name": "last_period_copilot_cost", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": "'0'" + }, + "storage_used_bytes": { + "name": "storage_used_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "billing_blocked": { + "name": "billing_blocked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "billing_blocked_reason": { + "name": "billing_blocked_reason", + "type": "billing_blocked_reason", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "limit_notifications": { + "name": "limit_notifications", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "user_stats_user_id_user_id_fk": { + "name": "user_stats_user_id_user_id_fk", + "tableFrom": "user_stats", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_stats_user_id_unique": { + "name": "user_stats_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_table_definitions": { + "name": "user_table_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "folder_id": { + "name": "folder_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "schema": { + "name": "schema", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "max_rows": { + "name": "max_rows", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10000 + }, + "row_count": { + "name": "row_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "rows_version": { + "name": "rows_version", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "schema_locked": { + "name": "schema_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "insert_locked": { + "name": "insert_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "update_locked": { + "name": "update_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "delete_locked": { + "name": "delete_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "user_table_def_workspace_id_idx": { + "name": "user_table_def_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_def_folder_id_idx": { + "name": "user_table_def_folder_id_idx", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_def_workspace_name_unique": { + "name": "user_table_def_workspace_name_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"user_table_definitions\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_def_archived_at_idx": { + "name": "user_table_def_archived_at_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_def_workspace_archived_partial_idx": { + "name": "user_table_def_workspace_archived_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"user_table_definitions\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_table_definitions_workspace_id_workspace_id_fk": { + "name": "user_table_definitions_workspace_id_workspace_id_fk", + "tableFrom": "user_table_definitions", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_table_definitions_folder_id_folder_id_fk": { + "name": "user_table_definitions_folder_id_folder_id_fk", + "tableFrom": "user_table_definitions", + "tableTo": "folder", + "columnsFrom": ["folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "user_table_definitions_created_by_user_id_fk": { + "name": "user_table_definitions_created_by_user_id_fk", + "tableFrom": "user_table_definitions", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_table_row_changes": { + "name": "user_table_row_changes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "user_table_row_changes_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "row_delta": { + "name": "row_delta", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "user_table_row_changes_table_id_idx": { + "name": "user_table_row_changes_table_id_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_table_row_changes_table_id_user_table_definitions_id_fk": { + "name": "user_table_row_changes_table_id_user_table_definitions_id_fk", + "tableFrom": "user_table_row_changes", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_table_row_secret_provenance": { + "name": "user_table_row_secret_provenance", + "schema": "", + "columns": { + "row_id": { + "name": "row_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "content_updated_at": { + "name": "content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entries": { + "name": "entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_table_row_secret_provenance_row_id_user_table_rows_id_fk": { + "name": "user_table_row_secret_provenance_row_id_user_table_rows_id_fk", + "tableFrom": "user_table_row_secret_provenance", + "tableTo": "user_table_rows", + "columnsFrom": ["row_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_table_row_secret_provenance_status_check": { + "name": "user_table_row_secret_provenance_status_check", + "value": "\"user_table_row_secret_provenance\".\"status\" IN ('exact', 'unknown')" + } + }, + "isRLSEnabled": false + }, + "public.user_table_rows": { + "name": "user_table_rows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "order_key": { + "name": "order_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_provenance_version": { + "name": "secret_provenance_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_table_rows_tenant_data_gin_idx": { + "name": "user_table_rows_tenant_data_gin_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"data\" jsonb_path_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "user_table_rows_workspace_table_idx": { + "name": "user_table_rows_workspace_table_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_rows_table_position_idx": { + "name": "user_table_rows_table_position_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_rows_table_order_key_idx": { + "name": "user_table_rows_table_order_key_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "order_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_rows_table_created_id_idx": { + "name": "user_table_rows_table_created_id_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "user_table_rows_table_id_id_idx": { + "name": "user_table_rows_table_id_id_idx", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_table_rows_table_id_user_table_definitions_id_fk": { + "name": "user_table_rows_table_id_user_table_definitions_id_fk", + "tableFrom": "user_table_rows", + "tableTo": "user_table_definitions", + "columnsFrom": ["table_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_table_rows_workspace_id_workspace_id_fk": { + "name": "user_table_rows_workspace_id_workspace_id_fk", + "tableFrom": "user_table_rows", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_table_rows_created_by_user_id_fk": { + "name": "user_table_rows_created_by_user_id_fk", + "tableFrom": "user_table_rows", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "verification_expires_at_idx": { + "name": "verification_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.waitlist": { + "name": "waitlist", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "waitlist_email_unique": { + "name": "waitlist_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webhook": { + "name": "webhook", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deployment_version_id": { + "name": "deployment_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_status": { + "name": "registration_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_generation": { + "name": "registration_generation", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "config_fingerprint": { + "name": "config_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prepared_at": { + "name": "prepared_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "routing_key": { + "name": "routing_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_config": { + "name": "provider_config", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "failed_count": { + "name": "failed_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "last_failed_at": { + "name": "last_failed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "path_deployment_unique": { + "name": "path_deployment_unique", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"webhook\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_workflow_deployment_idx": { + "name": "webhook_workflow_deployment_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_routing_key_active_idx": { + "name": "webhook_routing_key_active_idx", + "columns": [ + { + "expression": "routing_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"webhook\".\"archived_at\" IS NULL AND \"webhook\".\"routing_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_archived_at_partial_idx": { + "name": "webhook_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"webhook\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_webhook_on_provider_is_active_workflow_id_deploym_bdeed5468": { + "name": "idx_webhook_on_provider_is_active_workflow_id_deploym_bdeed5468", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_webhook_on_workflow_id_block_id_updated_at_desc": { + "name": "idx_webhook_on_workflow_id_block_id_updated_at_desc", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_active_registration_unique": { + "name": "webhook_active_registration_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"webhook\".\"registration_status\" = 'active' AND \"webhook\".\"block_id\" IS NOT NULL AND \"webhook\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_candidate_registration_unique": { + "name": "webhook_candidate_registration_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"webhook\".\"registration_status\" = 'candidate' AND \"webhook\".\"block_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "webhook_registration_status_generation_idx": { + "name": "webhook_registration_status_generation_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "registration_status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "registration_generation", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_workflow_id_workflow_id_fk": { + "name": "webhook_workflow_id_workflow_id_fk", + "tableFrom": "webhook", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "webhook_deployment_version_id_workflow_deployment_version_id_fk": { + "name": "webhook_deployment_version_id_workflow_deployment_version_id_fk", + "tableFrom": "webhook", + "tableTo": "workflow_deployment_version", + "columnsFrom": ["deployment_version_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "webhook_registration_status_check": { + "name": "webhook_registration_status_check", + "value": "\"webhook\".\"registration_status\" IS NULL OR \"webhook\".\"registration_status\" IN ('active', 'candidate', 'retired', 'orphaned')" + }, + "webhook_registration_generation_check": { + "name": "webhook_registration_generation_check", + "value": "\"webhook\".\"registration_generation\" IS NULL OR \"webhook\".\"registration_generation\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.webhook_path_claim": { + "name": "webhook_path_claim", + "schema": "", + "columns": { + "path": { + "name": "path", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "webhook_path_claim_workflow_idx": { + "name": "webhook_path_claim_workflow_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_path_claim_workflow_id_workflow_id_fk": { + "name": "webhook_path_claim_workflow_id_workflow_id_fk", + "tableFrom": "webhook_path_claim", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "webhook_path_claim_generation_check": { + "name": "webhook_path_claim_generation_check", + "value": "\"webhook_path_claim\".\"generation\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.workflow": { + "name": "workflow", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "folder_id": { + "name": "folder_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_synced": { + "name": "last_synced", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "is_deployed": { + "name": "is_deployed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deployed_at": { + "name": "deployed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "is_public_api": { + "name": "is_public_api", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "locked": { + "name": "locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "fork_sync_excluded": { + "name": "fork_sync_excluded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "run_count": { + "name": "run_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workflow_user_id_idx": { + "name": "workflow_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_workspace_id_idx": { + "name": "workflow_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_user_workspace_idx": { + "name": "workflow_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_workspace_folder_name_active_unique": { + "name": "workflow_workspace_folder_name_active_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"folder_id\", '')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_folder_sort_idx": { + "name": "workflow_folder_sort_idx", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_active_workspace_sort_idx": { + "name": "workflow_active_workspace_sort_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_archived_at_idx": { + "name": "workflow_archived_at_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_workspace_archived_partial_idx": { + "name": "workflow_workspace_archived_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_user_id_user_id_fk": { + "name": "workflow_user_id_user_id_fk", + "tableFrom": "workflow", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_workspace_id_workspace_id_fk": { + "name": "workflow_workspace_id_workspace_id_fk", + "tableFrom": "workflow", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_folder_id_folder_id_fk": { + "name": "workflow_folder_id_folder_id_fk", + "tableFrom": "workflow", + "tableTo": "folder", + "columnsFrom": ["folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_blocks": { + "name": "workflow_blocks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "position_x": { + "name": "position_x", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "position_y": { + "name": "position_y", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "horizontal_handles": { + "name": "horizontal_handles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_wide": { + "name": "is_wide", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "advanced_mode": { + "name": "advanced_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "trigger_mode": { + "name": "trigger_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "error_enabled": { + "name": "error_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "retry": { + "name": "retry", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "locked": { + "name": "locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "height": { + "name": "height", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "sub_blocks": { + "name": "sub_blocks", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "outputs": { + "name": "outputs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_blocks_workflow_id_idx": { + "name": "workflow_blocks_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_blocks_type_idx": { + "name": "workflow_blocks_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_blocks_workflow_id_workflow_id_fk": { + "name": "workflow_blocks_workflow_id_workflow_id_fk", + "tableFrom": "workflow_blocks", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_checkpoints": { + "name": "workflow_checkpoints", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_state": { + "name": "workflow_state", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_checkpoints_user_id_idx": { + "name": "workflow_checkpoints_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_workflow_id_idx": { + "name": "workflow_checkpoints_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_chat_id_idx": { + "name": "workflow_checkpoints_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_message_id_idx": { + "name": "workflow_checkpoints_message_id_idx", + "columns": [ + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_user_workflow_idx": { + "name": "workflow_checkpoints_user_workflow_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_workflow_chat_idx": { + "name": "workflow_checkpoints_workflow_chat_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_created_at_idx": { + "name": "workflow_checkpoints_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_chat_created_at_idx": { + "name": "workflow_checkpoints_chat_created_at_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_checkpoints_user_id_user_id_fk": { + "name": "workflow_checkpoints_user_id_user_id_fk", + "tableFrom": "workflow_checkpoints", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_checkpoints_workflow_id_workflow_id_fk": { + "name": "workflow_checkpoints_workflow_id_workflow_id_fk", + "tableFrom": "workflow_checkpoints", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_checkpoints_chat_id_copilot_chats_id_fk": { + "name": "workflow_checkpoints_chat_id_copilot_chats_id_fk", + "tableFrom": "workflow_checkpoints", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_deployment_operation": { + "name": "workflow_deployment_operation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deployment_version_id": { + "name": "deployment_version_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "previous_active_version_id": { + "name": "previous_active_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "protocol_version": { + "name": "protocol_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "generation": { + "name": "generation", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'preparing'" + }, + "component_readiness": { + "name": "component_readiness", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_deployment_operation_workflow_generation_unique": { + "name": "workflow_deployment_operation_workflow_generation_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generation", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_operation_workflow_idempotency_unique": { + "name": "workflow_deployment_operation_workflow_idempotency_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow_deployment_operation\".\"idempotency_key\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_operation_workflow_in_flight_unique": { + "name": "workflow_deployment_operation_workflow_in_flight_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow_deployment_operation\".\"status\" IN ('preparing', 'activating')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_operation_workflow_status_idx": { + "name": "workflow_deployment_operation_workflow_status_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_operation_deployment_version_idx": { + "name": "workflow_deployment_operation_deployment_version_idx", + "columns": [ + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_operation_workflow_version_generation_idx": { + "name": "workflow_deployment_operation_workflow_version_generation_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generation", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_deployment_operation_workflow_id_workflow_id_fk": { + "name": "workflow_deployment_operation_workflow_id_workflow_id_fk", + "tableFrom": "workflow_deployment_operation", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_deployment_operation_deployment_version_id_workflow_deployment_version_id_fk": { + "name": "workflow_deployment_operation_deployment_version_id_workflow_deployment_version_id_fk", + "tableFrom": "workflow_deployment_operation", + "tableTo": "workflow_deployment_version", + "columnsFrom": ["deployment_version_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_deployment_operation_previous_active_version_id_workflow_deployment_version_id_fk": { + "name": "workflow_deployment_operation_previous_active_version_id_workflow_deployment_version_id_fk", + "tableFrom": "workflow_deployment_operation", + "tableTo": "workflow_deployment_version", + "columnsFrom": ["previous_active_version_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workflow_deployment_operation_action_check": { + "name": "workflow_deployment_operation_action_check", + "value": "\"workflow_deployment_operation\".\"action\" IN ('deploy', 'activate')" + }, + "workflow_deployment_operation_status_check": { + "name": "workflow_deployment_operation_status_check", + "value": "\"workflow_deployment_operation\".\"status\" IN ('preparing', 'activating', 'active', 'failed', 'superseded')" + }, + "workflow_deployment_operation_generation_check": { + "name": "workflow_deployment_operation_generation_check", + "value": "\"workflow_deployment_operation\".\"generation\" > 0" + }, + "workflow_deployment_operation_protocol_version_check": { + "name": "workflow_deployment_operation_protocol_version_check", + "value": "\"workflow_deployment_operation\".\"protocol_version\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.workflow_deployment_version": { + "name": "workflow_deployment_version", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workflow_deployment_version_workflow_version_unique": { + "name": "workflow_deployment_version_workflow_version_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_version_workflow_active_idx": { + "name": "workflow_deployment_version_workflow_active_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_deployment_version_created_at_idx": { + "name": "workflow_deployment_version_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_deployment_version_workflow_id_workflow_id_fk": { + "name": "workflow_deployment_version_workflow_id_workflow_id_fk", + "tableFrom": "workflow_deployment_version", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_edges": { + "name": "workflow_edges", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_block_id": { + "name": "source_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_block_id": { + "name": "target_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_handle": { + "name": "source_handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_handle": { + "name": "target_handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_edges_workflow_id_idx": { + "name": "workflow_edges_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_workflow_source_idx": { + "name": "workflow_edges_workflow_source_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_workflow_target_idx": { + "name": "workflow_edges_workflow_target_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_edges_workflow_id_workflow_id_fk": { + "name": "workflow_edges_workflow_id_workflow_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_edges_source_block_id_workflow_blocks_id_fk": { + "name": "workflow_edges_source_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow_blocks", + "columnsFrom": ["source_block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_edges_target_block_id_workflow_blocks_id_fk": { + "name": "workflow_edges_target_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow_blocks", + "columnsFrom": ["target_block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_execution_logs": { + "name": "workflow_execution_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_snapshot_id": { + "name": "state_snapshot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deployment_version_id": { + "name": "deployment_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "execution_deadline_at": { + "name": "execution_deadline_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_duration_ms": { + "name": "total_duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "execution_data": { + "name": "execution_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "cost_total": { + "name": "cost_total", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "models_used": { + "name": "models_used", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "files": { + "name": "files", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_execution_logs_workflow_id_idx": { + "name": "workflow_execution_logs_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_state_snapshot_id_idx": { + "name": "workflow_execution_logs_state_snapshot_id_idx", + "columns": [ + { + "expression": "state_snapshot_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_deployment_version_id_idx": { + "name": "workflow_execution_logs_deployment_version_id_idx", + "columns": [ + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_trigger_idx": { + "name": "workflow_execution_logs_trigger_idx", + "columns": [ + { + "expression": "trigger", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_level_idx": { + "name": "workflow_execution_logs_level_idx", + "columns": [ + { + "expression": "level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_started_at_idx": { + "name": "workflow_execution_logs_started_at_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_execution_id_unique": { + "name": "workflow_execution_logs_execution_id_unique", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workflow_started_at_idx": { + "name": "workflow_execution_logs_workflow_started_at_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workspace_started_at_idx": { + "name": "workflow_execution_logs_workspace_started_at_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workspace_activity_idx": { + "name": "workflow_execution_logs_workspace_activity_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "total_duration_ms", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workspace_started_at_id_desc_idx": { + "name": "workflow_execution_logs_workspace_started_at_id_desc_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"started_at\" DESC NULLS LAST", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "\"id\" DESC", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workspace_cost_total_idx": { + "name": "workflow_execution_logs_workspace_cost_total_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cost_total", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_models_used_idx": { + "name": "workflow_execution_logs_models_used_idx", + "columns": [ + { + "expression": "models_used", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "workflow_execution_logs_workspace_ended_at_id_idx": { + "name": "workflow_execution_logs_workspace_ended_at_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date_trunc('milliseconds', \"ended_at\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_running_started_at_idx": { + "name": "workflow_execution_logs_running_started_at_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "status = 'running'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_running_deadline_idx": { + "name": "workflow_execution_logs_running_deadline_idx", + "columns": [ + { + "expression": "execution_deadline_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_execution_logs\".\"status\" = 'running' AND \"workflow_execution_logs\".\"execution_deadline_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_redacting_started_at_idx": { + "name": "workflow_execution_logs_redacting_started_at_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "status = 'redacting'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_redacting_deadline_idx": { + "name": "workflow_execution_logs_redacting_deadline_idx", + "columns": [ + { + "expression": "execution_deadline_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_execution_logs\".\"status\" = 'redacting' AND \"workflow_execution_logs\".\"execution_deadline_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_completed_ended_at_idx": { + "name": "workflow_execution_logs_completed_ended_at_idx", + "columns": [ + { + "expression": "ended_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_execution_logs\".\"status\" = 'completed' AND \"workflow_execution_logs\".\"level\" = 'info' AND \"workflow_execution_logs\".\"ended_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_execution_logs_workflow_id_workflow_id_fk": { + "name": "workflow_execution_logs_workflow_id_workflow_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workflow_execution_logs_workspace_id_workspace_id_fk": { + "name": "workflow_execution_logs_workspace_id_workspace_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_execution_logs_state_snapshot_id_workflow_execution_snapshots_id_fk": { + "name": "workflow_execution_logs_state_snapshot_id_workflow_execution_snapshots_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workflow_execution_snapshots", + "columnsFrom": ["state_snapshot_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_execution_logs_deployment_version_id_workflow_deployment_version_id_fk": { + "name": "workflow_execution_logs_deployment_version_id_workflow_deployment_version_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workflow_deployment_version", + "columnsFrom": ["deployment_version_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_execution_snapshots": { + "name": "workflow_execution_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state_hash": { + "name": "state_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_data": { + "name": "state_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_snapshots_workflow_id_idx": { + "name": "workflow_snapshots_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_hash_idx": { + "name": "workflow_snapshots_hash_idx", + "columns": [ + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_workflow_hash_idx": { + "name": "workflow_snapshots_workflow_hash_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_created_at_idx": { + "name": "workflow_snapshots_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_execution_snapshots_workflow_id_workflow_id_fk": { + "name": "workflow_execution_snapshots_workflow_id_workflow_id_fk", + "tableFrom": "workflow_execution_snapshots", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_mcp_server": { + "name": "workflow_mcp_server", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_mcp_server_workspace_id_idx": { + "name": "workflow_mcp_server_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_server_created_by_idx": { + "name": "workflow_mcp_server_created_by_idx", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_server_deleted_at_idx": { + "name": "workflow_mcp_server_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_server_workspace_deleted_partial_idx": { + "name": "workflow_mcp_server_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_mcp_server\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_mcp_server_workspace_id_workspace_id_fk": { + "name": "workflow_mcp_server_workspace_id_workspace_id_fk", + "tableFrom": "workflow_mcp_server", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_mcp_server_created_by_user_id_fk": { + "name": "workflow_mcp_server_created_by_user_id_fk", + "tableFrom": "workflow_mcp_server", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_mcp_tool": { + "name": "workflow_mcp_tool", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_description": { + "name": "tool_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parameter_schema": { + "name": "parameter_schema", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "parameter_description_overrides": { + "name": "parameter_description_overrides", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'::json" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_mcp_tool_server_id_idx": { + "name": "workflow_mcp_tool_server_id_idx", + "columns": [ + { + "expression": "server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_tool_workflow_id_idx": { + "name": "workflow_mcp_tool_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_tool_server_workflow_unique": { + "name": "workflow_mcp_tool_server_workflow_unique", + "columns": [ + { + "expression": "server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow_mcp_tool\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_mcp_tool_archived_at_partial_idx": { + "name": "workflow_mcp_tool_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_mcp_tool\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_mcp_tool_server_id_workflow_mcp_server_id_fk": { + "name": "workflow_mcp_tool_server_id_workflow_mcp_server_id_fk", + "tableFrom": "workflow_mcp_tool", + "tableTo": "workflow_mcp_server", + "columnsFrom": ["server_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_mcp_tool_workflow_id_workflow_id_fk": { + "name": "workflow_mcp_tool_workflow_id_workflow_id_fk", + "tableFrom": "workflow_mcp_tool", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_schedule": { + "name": "workflow_schedule", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deployment_version_id": { + "name": "deployment_version_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deployment_operation_id": { + "name": "deployment_operation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cron_expression": { + "name": "cron_expression", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_ran_at": { + "name": "last_ran_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_queued_at": { + "name": "last_queued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "trigger_type": { + "name": "trigger_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'UTC'" + }, + "failed_count": { + "name": "failed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "infra_retry_count": { + "name": "infra_retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_failed_at": { + "name": "last_failed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'workflow'" + }, + "job_title": { + "name": "job_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lifecycle": { + "name": "lifecycle", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'persistent'" + }, + "success_condition": { + "name": "success_condition", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "max_runs": { + "name": "max_runs", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "run_count": { + "name": "run_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "source_chat_id": { + "name": "source_chat_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_task_name": { + "name": "source_task_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_user_id": { + "name": "source_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_workspace_id": { + "name": "source_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_scope": { + "name": "secret_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'all'" + }, + "mounted_secrets": { + "name": "mounted_secrets", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "job_history": { + "name": "job_history", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "contexts": { + "name": "contexts", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "excluded_dates": { + "name": "excluded_dates", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_schedule_workflow_block_deployment_unique": { + "name": "workflow_schedule_workflow_block_deployment_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow_schedule\".\"archived_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_schedule_workflow_deployment_idx": { + "name": "workflow_schedule_workflow_deployment_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_schedule_archived_at_partial_idx": { + "name": "workflow_schedule_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_schedule\".\"archived_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_workflow_schedule_on_source_workspace_id_source_t_c07f3bba6": { + "name": "idx_workflow_schedule_on_source_workspace_id_source_t_c07f3bba6", + "columns": [ + { + "expression": "source_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_schedule_due_workflow_idx": { + "name": "workflow_schedule_due_workflow_idx", + "columns": [ + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deployment_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_schedule\".\"archived_at\" IS NULL AND \"workflow_schedule\".\"status\" NOT IN ('disabled', 'completed') AND (\"workflow_schedule\".\"source_type\" = 'workflow' OR \"workflow_schedule\".\"source_type\" IS NULL)", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_schedule_due_job_idx": { + "name": "workflow_schedule_due_job_idx", + "columns": [ + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workflow_schedule\".\"archived_at\" IS NULL AND \"workflow_schedule\".\"status\" NOT IN ('disabled', 'completed') AND \"workflow_schedule\".\"source_type\" = 'job'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_schedule_workflow_id_workflow_id_fk": { + "name": "workflow_schedule_workflow_id_workflow_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_schedule_deployment_version_id_workflow_deployment_version_id_fk": { + "name": "workflow_schedule_deployment_version_id_workflow_deployment_version_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workflow_deployment_version", + "columnsFrom": ["deployment_version_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_schedule_deployment_operation_id_workflow_deployment_operation_id_fk": { + "name": "workflow_schedule_deployment_operation_id_workflow_deployment_operation_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workflow_deployment_operation", + "columnsFrom": ["deployment_operation_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workflow_schedule_source_user_id_user_id_fk": { + "name": "workflow_schedule_source_user_id_user_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "user", + "columnsFrom": ["source_user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_schedule_source_workspace_id_workspace_id_fk": { + "name": "workflow_schedule_source_workspace_id_workspace_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workspace", + "columnsFrom": ["source_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_subflows": { + "name": "workflow_subflows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_subflows_workflow_id_idx": { + "name": "workflow_subflows_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_subflows_workflow_type_idx": { + "name": "workflow_subflows_workflow_type_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_subflows_workflow_id_workflow_id_fk": { + "name": "workflow_subflows_workflow_id_workflow_id_fk", + "tableFrom": "workflow_subflows", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_test": { + "name": "workflow_test", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body_file_id": { + "name": "body_file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cases": { + "name": "cases", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "source_hash": { + "name": "source_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_test_workspace_name_unique": { + "name": "workflow_test_workspace_name_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workflow_test\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_test_body_file_unique": { + "name": "workflow_test_body_file_unique", + "columns": [ + { + "expression": "body_file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_test_workspace_id_workspace_id_fk": { + "name": "workflow_test_workspace_id_workspace_id_fk", + "tableFrom": "workflow_test", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_test_body_file_id_workspace_files_id_fk": { + "name": "workflow_test_body_file_id_workspace_files_id_fk", + "tableFrom": "workflow_test", + "tableTo": "workspace_files", + "columnsFrom": ["body_file_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_test_created_by_user_id_user_id_fk": { + "name": "workflow_test_created_by_user_id_user_id_fk", + "tableFrom": "workflow_test", + "tableTo": "user", + "columnsFrom": ["created_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_test_run": { + "name": "workflow_test_run", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "test_id": { + "name": "test_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "passed": { + "name": "passed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "failed": { + "name": "failed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skipped": { + "name": "skipped", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "report": { + "name": "report", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_hash": { + "name": "source_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ran_against": { + "name": "ran_against", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "progress": { + "name": "progress", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "triggered_by_actor": { + "name": "triggered_by_actor", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "triggered_by_user_id": { + "name": "triggered_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workflow_test_run_test_version_started_idx": { + "name": "workflow_test_run_test_version_started_idx", + "columns": [ + { + "expression": "test_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_test_run_test_id_workflow_test_id_fk": { + "name": "workflow_test_run_test_id_workflow_test_id_fk", + "tableFrom": "workflow_test_run", + "tableTo": "workflow_test", + "columnsFrom": ["test_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_test_run_workspace_id_workspace_id_fk": { + "name": "workflow_test_run_workspace_id_workspace_id_fk", + "tableFrom": "workflow_test_run", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_test_run_triggered_by_user_id_user_id_fk": { + "name": "workflow_test_run_triggered_by_user_id_user_id_fk", + "tableFrom": "workflow_test_run", + "tableTo": "user", + "columnsFrom": ["triggered_by_user_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workflow_test_run_version_check": { + "name": "workflow_test_run_version_check", + "value": "\"workflow_test_run\".\"version\" IN ('draft', 'deployed')" + }, + "workflow_test_run_status_check": { + "name": "workflow_test_run_status_check", + "value": "\"workflow_test_run\".\"status\" IN ('running', 'passed', 'failed', 'error')" + }, + "workflow_test_run_completed_check": { + "name": "workflow_test_run_completed_check", + "value": "(\"workflow_test_run\".\"status\" = 'running') = (\"workflow_test_run\".\"completed_at\" IS NULL)" + } + }, + "isRLSEnabled": false + }, + "public.workspace": { + "name": "workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#33C482'" + }, + "logo_url": { + "name": "logo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_mode": { + "name": "workspace_mode", + "type": "workspace_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'grandfathered_shared'" + }, + "billed_account_user_id": { + "name": "billed_account_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_used_bytes": { + "name": "storage_used_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "allow_personal_api_keys": { + "name": "allow_personal_api_keys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "inbox_enabled": { + "name": "inbox_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "inbox_address": { + "name": "inbox_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inbox_provider_id": { + "name": "inbox_provider_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inbox_secret_scope": { + "name": "inbox_secret_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'all'" + }, + "inbox_mounted_secrets": { + "name": "inbox_mounted_secrets", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "organization_assigned_at": { + "name": "organization_assigned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "forked_from_workspace_id": { + "name": "forked_from_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fork_sync_new_workflows_excluded": { + "name": "fork_sync_new_workflows_excluded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_owner_id_idx": { + "name": "workspace_owner_id_idx", + "columns": [ + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_organization_id_idx": { + "name": "workspace_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_mode_idx": { + "name": "workspace_mode_idx", + "columns": [ + { + "expression": "workspace_mode", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_forked_from_workspace_id_idx": { + "name": "workspace_forked_from_workspace_id_idx", + "columns": [ + { + "expression": "forked_from_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_inbox_provider_id_idx": { + "name": "workspace_inbox_provider_id_idx", + "columns": [ + { + "expression": "inbox_provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workspace\".\"inbox_provider_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_owner_id_user_id_fk": { + "name": "workspace_owner_id_user_id_fk", + "tableFrom": "workspace", + "tableTo": "user", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_organization_id_organization_id_fk": { + "name": "workspace_organization_id_organization_id_fk", + "tableFrom": "workspace", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_billed_account_user_id_user_id_fk": { + "name": "workspace_billed_account_user_id_user_id_fk", + "tableFrom": "workspace", + "tableTo": "user", + "columnsFrom": ["billed_account_user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workspace_forked_from_workspace_id_workspace_id_fk": { + "name": "workspace_forked_from_workspace_id_workspace_id_fk", + "tableFrom": "workspace", + "tableTo": "workspace", + "columnsFrom": ["forked_from_workspace_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workspace_storage_used_bytes_non_negative": { + "name": "workspace_storage_used_bytes_non_negative", + "value": "\"workspace\".\"storage_used_bytes\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.workspace_byok_keys": { + "name": "workspace_byok_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_byok_workspace_provider_idx": { + "name": "workspace_byok_workspace_provider_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_byok_keys_workspace_id_workspace_id_fk": { + "name": "workspace_byok_keys_workspace_id_workspace_id_fk", + "tableFrom": "workspace_byok_keys", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_byok_keys_created_by_user_id_fk": { + "name": "workspace_byok_keys_created_by_user_id_fk", + "tableFrom": "workspace_byok_keys", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_environment": { + "name": "workspace_environment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_environment_workspace_unique": { + "name": "workspace_environment_workspace_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_environment_workspace_id_workspace_id_fk": { + "name": "workspace_environment_workspace_id_workspace_id_fk", + "tableFrom": "workspace_environment", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file": { + "name": "workspace_file", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uploaded_by": { + "name": "uploaded_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_file_workspace_id_idx": { + "name": "workspace_file_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_deleted_at_idx": { + "name": "workspace_file_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_workspace_deleted_partial_idx": { + "name": "workspace_file_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_workspace_id_workspace_id_fk": { + "name": "workspace_file_workspace_id_workspace_id_fk", + "tableFrom": "workspace_file", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_file_uploaded_by_user_id_fk": { + "name": "workspace_file_uploaded_by_user_id_fk", + "tableFrom": "workspace_file", + "tableTo": "user", + "columnsFrom": ["uploaded_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "workspace_file_key_unique": { + "name": "workspace_file_key_unique", + "nullsNotDistinct": false, + "columns": ["key"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_collab_state": { + "name": "workspace_file_collab_state", + "schema": "", + "columns": { + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "doc_state": { + "name": "doc_state", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "source_hash": { + "name": "source_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workspace_file_collab_state_file_id_workspace_files_id_fk": { + "name": "workspace_file_collab_state_file_id_workspace_files_id_fk", + "tableFrom": "workspace_file_collab_state", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_backfill": { + "name": "workspace_file_search_backfill", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "after_workspace_id": { + "name": "after_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "after_file_id": { + "name": "after_file_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_build": { + "name": "workspace_file_search_build", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workspace_file_search_build_file_idx": { + "name": "workspace_file_search_build_file_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_build_cleanup_idx": { + "name": "workspace_file_search_build_cleanup_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_build\".\"expires_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_chunk": { + "name": "workspace_file_search_chunk", + "schema": "", + "columns": { + "build_id": { + "name": "build_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ordinal": { + "name": "ordinal", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "line_start": { + "name": "line_start", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "fragment": { + "name": "fragment", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "overlap": { + "name": "overlap", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "workspace_file_search_chunk_line_idx": { + "name": "workspace_file_search_chunk_line_idx", + "columns": [ + { + "expression": "build_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "line_start", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "ordinal", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_chunk_content_idx": { + "name": "workspace_file_search_chunk_content_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "content", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": true, + "method": "gin", + "with": { + "fastupdate": "off" + } + } + }, + "foreignKeys": { + "workspace_file_search_chunk_build_id_workspace_file_search_build_id_fk": { + "name": "workspace_file_search_chunk_build_id_workspace_file_search_build_id_fk", + "tableFrom": "workspace_file_search_chunk", + "tableTo": "workspace_file_search_build", + "columnsFrom": ["build_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "workspace_file_search_chunk_pk": { + "name": "workspace_file_search_chunk_pk", + "columns": ["build_id", "ordinal"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workspace_file_search_chunk_content_size": { + "name": "workspace_file_search_chunk_content_size", + "value": "octet_length(\"workspace_file_search_chunk\".\"content\") <= 8192" + }, + "workspace_file_search_chunk_position": { + "name": "workspace_file_search_chunk_position", + "value": "\"workspace_file_search_chunk\".\"ordinal\" >= 0 AND \"workspace_file_search_chunk\".\"line_start\" > 0 AND \"workspace_file_search_chunk\".\"overlap\" BETWEEN 0 AND 2" + } + }, + "isRLSEnabled": false + }, + "public.workspace_file_search_dispatch_queue": { + "name": "workspace_file_search_dispatch_queue", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "enqueued_at": { + "name": "enqueued_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_dispatched_at": { + "name": "last_dispatched_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_file_search_dispatch_queue_schedule_idx": { + "name": "workspace_file_search_dispatch_queue_schedule_idx", + "columns": [ + { + "expression": "last_dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "first" + }, + { + "expression": "enqueued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_search_queue_workspace_fk": { + "name": "workspace_file_search_queue_workspace_fk", + "tableFrom": "workspace_file_search_dispatch_queue", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_index": { + "name": "workspace_file_search_index", + "schema": "", + "columns": { + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "workspace_file_search_index_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "partial": { + "name": "partial", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "line_count": { + "name": "line_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "indexed_bytes": { + "name": "indexed_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "dispatched_at": { + "name": "dispatched_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_file_search_index_workspace_status_idx": { + "name": "workspace_file_search_index_workspace_status_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_content_updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_index_pending_dispatch_idx": { + "name": "workspace_file_search_index_pending_dispatch_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_content_updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_index\".\"status\" = 'pending' AND \"workspace_file_search_index\".\"dispatched_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_index_active_dispatch_idx": { + "name": "workspace_file_search_index_active_dispatch_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_index\".\"status\" = 'pending' AND \"workspace_file_search_index\".\"dispatched_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_search_index_file_fk": { + "name": "workspace_file_search_index_file_fk", + "tableFrom": "workspace_file_search_index", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_file_search_index_workspace_fk": { + "name": "workspace_file_search_index_workspace_fk", + "tableFrom": "workspace_file_search_index", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "workspace_file_search_index_pk": { + "name": "workspace_file_search_index_pk", + "columns": ["file_id", "source_content_updated_at"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_revision": { + "name": "workspace_file_search_revision", + "schema": "", + "columns": { + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "workspace_file_search_index_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "build_id": { + "name": "build_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_reason": { + "name": "failure_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "line_count": { + "name": "line_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "indexed_bytes": { + "name": "indexed_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "chunk_count": { + "name": "chunk_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "dispatched_at": { + "name": "dispatched_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "handoff_expires_at": { + "name": "handoff_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_file_search_revision_workspace_status_idx": { + "name": "workspace_file_search_revision_workspace_status_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_revision_build_idx": { + "name": "workspace_file_search_revision_build_idx", + "columns": [ + { + "expression": "build_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_revision_pending_idx": { + "name": "workspace_file_search_revision_pending_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_content_updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_revision\".\"status\" = 'pending' AND \"workspace_file_search_revision\".\"dispatched_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_revision_active_idx": { + "name": "workspace_file_search_revision_active_idx", + "columns": [ + { + "expression": "dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_revision\".\"status\" = 'pending' AND \"workspace_file_search_revision\".\"dispatched_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_search_revision_file_id_workspace_files_id_fk": { + "name": "workspace_file_search_revision_file_id_workspace_files_id_fk", + "tableFrom": "workspace_file_search_revision", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_file_search_revision_build_id_workspace_file_search_build_id_fk": { + "name": "workspace_file_search_revision_build_id_workspace_file_search_build_id_fk", + "tableFrom": "workspace_file_search_revision", + "tableTo": "workspace_file_search_build", + "columnsFrom": ["build_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_search_segment": { + "name": "workspace_file_search_segment", + "schema": "", + "columns": { + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "line_number": { + "name": "line_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "segment_number": { + "name": "segment_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "segment_start": { + "name": "segment_start", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "line_length": { + "name": "line_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "workspace_file_search_segment_workspace_revision_idx": { + "name": "workspace_file_search_segment_workspace_revision_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_content_updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_search_segment_workspace_content_trgm_idx": { + "name": "workspace_file_search_segment_workspace_content_trgm_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "content", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_search_segment_file_fk": { + "name": "workspace_file_search_segment_file_fk", + "tableFrom": "workspace_file_search_segment", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_file_search_segment_workspace_fk": { + "name": "workspace_file_search_segment_workspace_fk", + "tableFrom": "workspace_file_search_segment", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "workspace_file_search_segment_pk": { + "name": "workspace_file_search_segment_pk", + "columns": ["file_id", "source_content_updated_at", "line_number", "segment_number"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_file_secret_provenance": { + "name": "workspace_file_secret_provenance", + "schema": "", + "columns": { + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "content_updated_at": { + "name": "content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entries": { + "name": "entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workspace_file_secret_provenance_file_id_workspace_files_id_fk": { + "name": "workspace_file_secret_provenance_file_id_workspace_files_id_fk", + "tableFrom": "workspace_file_secret_provenance", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workspace_file_secret_provenance_status_check": { + "name": "workspace_file_secret_provenance_status_check", + "value": "\"workspace_file_secret_provenance\".\"status\" IN ('exact', 'unknown', 'unrecorded')" + } + }, + "isRLSEnabled": false + }, + "public.workspace_file_version": { + "name": "workspace_file_version", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "superseded_at": { + "name": "superseded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "workspace_file_version_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "author_user_ids": { + "name": "author_user_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "restored_from_version": { + "name": "restored_from_version", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "secret_provenance_status": { + "name": "secret_provenance_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret_provenance_entries": { + "name": "secret_provenance_entries", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_file_version_file_version_unique": { + "name": "workspace_file_version_file_version_unique", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_version_key_unique": { + "name": "workspace_file_version_key_unique", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_version_workspace_id_idx": { + "name": "workspace_file_version_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_file_version_workspace_superseded_idx": { + "name": "workspace_file_version_workspace_superseded_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "superseded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_file_version\".\"superseded_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_file_version_file_id_workspace_files_id_fk": { + "name": "workspace_file_version_file_id_workspace_files_id_fk", + "tableFrom": "workspace_file_version", + "tableTo": "workspace_files", + "columnsFrom": ["file_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_file_version_workspace_id_workspace_id_fk": { + "name": "workspace_file_version_workspace_id_workspace_id_fk", + "tableFrom": "workspace_file_version", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workspace_file_version_provenance_status_check": { + "name": "workspace_file_version_provenance_status_check", + "value": "\"workspace_file_version\".\"secret_provenance_status\" IS NULL OR \"workspace_file_version\".\"secret_provenance_status\" IN ('exact', 'unknown', 'unrecorded')" + } + }, + "isRLSEnabled": false + }, + "public.workspace_files": { + "name": "workspace_files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "folder_id": { + "name": "folder_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "context": { + "name": "context", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_name": { + "name": "original_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "content_updated_at": { + "name": "content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "date_trunc('milliseconds', now())" + }, + "secret_provenance_version": { + "name": "secret_provenance_version", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workspace_files_key_active_unique": { + "name": "workspace_files_key_active_unique", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workspace_files\".\"deleted_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_workspace_folder_name_active_unique": { + "name": "workspace_files_workspace_folder_name_active_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"folder_id\", '')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "original_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workspace_files\".\"deleted_at\" IS NULL AND \"workspace_files\".\"context\" = 'workspace' AND \"workspace_files\".\"workspace_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_workspace_active_keyset_idx": { + "name": "workspace_files_workspace_active_keyset_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_files\".\"deleted_at\" IS NULL AND \"workspace_files\".\"context\" = 'workspace' AND \"workspace_files\".\"workspace_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_chat_display_name_unique": { + "name": "workspace_files_chat_display_name_unique", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"workspace_files\".\"context\" = 'mothership' AND \"workspace_files\".\"chat_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_organization_id_idx": { + "name": "workspace_files_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_key_idx": { + "name": "workspace_files_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_user_id_idx": { + "name": "workspace_files_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_workspace_id_idx": { + "name": "workspace_files_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_folder_id_idx": { + "name": "workspace_files_folder_id_idx", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_context_idx": { + "name": "workspace_files_context_idx", + "columns": [ + { + "expression": "context", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_chat_id_idx": { + "name": "workspace_files_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_deleted_at_idx": { + "name": "workspace_files_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_files_workspace_deleted_partial_idx": { + "name": "workspace_files_workspace_deleted_partial_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_files\".\"deleted_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_files_user_id_user_id_fk": { + "name": "workspace_files_user_id_user_id_fk", + "tableFrom": "workspace_files", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_files_workspace_id_workspace_id_fk": { + "name": "workspace_files_workspace_id_workspace_id_fk", + "tableFrom": "workspace_files", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_files_organization_id_organization_id_fk": { + "name": "workspace_files_organization_id_organization_id_fk", + "tableFrom": "workspace_files", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_files_folder_id_folder_id_fk": { + "name": "workspace_files_folder_id_folder_id_fk", + "tableFrom": "workspace_files", + "tableTo": "folder", + "columnsFrom": ["folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "workspace_files_chat_id_copilot_chats_id_fk": { + "name": "workspace_files_chat_id_copilot_chats_id_fk", + "tableFrom": "workspace_files", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "workspace_files_organization_binding_check": { + "name": "workspace_files_organization_binding_check", + "value": "\"workspace_files\".\"organization_id\" IS NULL OR (\"workspace_files\".\"workspace_id\" IS NULL AND \"workspace_files\".\"context\" = 'knowledge-base' AND \"workspace_files\".\"folder_id\" IS NULL AND \"workspace_files\".\"chat_id\" IS NULL)" + } + }, + "isRLSEnabled": false + }, + "public.workspace_fork_block_map": { + "name": "workspace_fork_block_map", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "child_workspace_id": { + "name": "child_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_workflow_id": { + "name": "parent_workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_block_id": { + "name": "parent_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "child_workflow_id": { + "name": "child_workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "child_block_id": { + "name": "child_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_fork_block_map_child_ws_parent_unique": { + "name": "workspace_fork_block_map_child_ws_parent_unique", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_block_map_child_ws_child_unique": { + "name": "workspace_fork_block_map_child_ws_child_unique", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "child_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_block_map_child_ws_parent_wf_idx": { + "name": "workspace_fork_block_map_child_ws_parent_wf_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_block_map_child_ws_child_wf_idx": { + "name": "workspace_fork_block_map_child_ws_child_wf_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "child_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_fork_block_map_child_workspace_id_workspace_id_fk": { + "name": "workspace_fork_block_map_child_workspace_id_workspace_id_fk", + "tableFrom": "workspace_fork_block_map", + "tableTo": "workspace", + "columnsFrom": ["child_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_fork_dependent_value": { + "name": "workspace_fork_dependent_value", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "child_workspace_id": { + "name": "child_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_workflow_id": { + "name": "target_workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_block_id": { + "name": "target_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sub_block_key": { + "name": "sub_block_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_fork_dependent_value_child_ws_wf_idx": { + "name": "workspace_fork_dependent_value_child_ws_wf_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_dependent_value_field_unique": { + "name": "workspace_fork_dependent_value_field_unique", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sub_block_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_fork_dependent_value_child_workspace_id_workspace_id_fk": { + "name": "workspace_fork_dependent_value_child_workspace_id_workspace_id_fk", + "tableFrom": "workspace_fork_dependent_value", + "tableTo": "workspace", + "columnsFrom": ["child_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_fork_promote_run": { + "name": "workspace_fork_promote_run", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "child_workspace_id": { + "name": "child_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_workspace_id": { + "name": "source_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_workspace_id": { + "name": "target_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "workspace_fork_promote_direction", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_fork_promote_run_child_ws_target_unique": { + "name": "workspace_fork_promote_run_child_ws_target_unique", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_promote_run_target_ws_idx": { + "name": "workspace_fork_promote_run_target_ws_idx", + "columns": [ + { + "expression": "target_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_fork_promote_run_child_workspace_id_workspace_id_fk": { + "name": "workspace_fork_promote_run_child_workspace_id_workspace_id_fk", + "tableFrom": "workspace_fork_promote_run", + "tableTo": "workspace", + "columnsFrom": ["child_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_fork_promote_run_created_by_user_id_fk": { + "name": "workspace_fork_promote_run_created_by_user_id_fk", + "tableFrom": "workspace_fork_promote_run", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_fork_resource_map": { + "name": "workspace_fork_resource_map", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "child_workspace_id": { + "name": "child_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "workspace_fork_resource_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "parent_resource_id": { + "name": "parent_resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "child_resource_id": { + "name": "child_resource_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_fork_resource_map_child_ws_idx": { + "name": "workspace_fork_resource_map_child_ws_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_resource_map_child_ws_type_idx": { + "name": "workspace_fork_resource_map_child_ws_type_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_resource_map_child_type_parent_unique": { + "name": "workspace_fork_resource_map_child_type_parent_unique", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_fork_resource_map_child_workspace_id_workspace_id_fk": { + "name": "workspace_fork_resource_map_child_workspace_id_workspace_id_fk", + "tableFrom": "workspace_fork_resource_map", + "tableTo": "workspace", + "columnsFrom": ["child_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_fork_resource_map_created_by_user_id_fk": { + "name": "workspace_fork_resource_map_created_by_user_id_fk", + "tableFrom": "workspace_fork_resource_map", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_fork_workflow_sync": { + "name": "workspace_fork_workflow_sync", + "schema": "", + "columns": { + "deployment_operation_id": { + "name": "deployment_operation_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "child_workspace_id": { + "name": "child_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_workflow_id": { + "name": "source_workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_workflow_id": { + "name": "target_workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_deployment_version_id": { + "name": "source_deployment_version_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "identity": { + "type": "always", + "name": "workspace_fork_workflow_sync_sequence_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "promote_run_id": { + "name": "promote_run_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "rollback_operation_id": { + "name": "rollback_operation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rolled_back_at": { + "name": "rolled_back_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workspace_fork_workflow_sync_baseline_idx": { + "name": "workspace_fork_workflow_sync_baseline_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_fork_workflow_sync\".\"activated_at\" IS NOT NULL AND \"workspace_fork_workflow_sync\".\"rolled_back_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_workflow_sync_run_idx": { + "name": "workspace_fork_workflow_sync_run_idx", + "columns": [ + { + "expression": "promote_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_workflow_sync_rollback_idx": { + "name": "workspace_fork_workflow_sync_rollback_idx", + "columns": [ + { + "expression": "rollback_operation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"workspace_fork_workflow_sync\".\"rollback_operation_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_workflow_sync_source_idx": { + "name": "workspace_fork_workflow_sync_source_idx", + "columns": [ + { + "expression": "source_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_workflow_sync_target_idx": { + "name": "workspace_fork_workflow_sync_target_idx", + "columns": [ + { + "expression": "target_workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_fork_workflow_sync_child_workspace_idx": { + "name": "workspace_fork_workflow_sync_child_workspace_idx", + "columns": [ + { + "expression": "child_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_fork_workflow_sync_child_workspace_id_workspace_id_fk": { + "name": "workspace_fork_workflow_sync_child_workspace_id_workspace_id_fk", + "tableFrom": "workspace_fork_workflow_sync", + "tableTo": "workspace", + "columnsFrom": ["child_workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_fork_workflow_sync_source_workflow_id_workflow_id_fk": { + "name": "workspace_fork_workflow_sync_source_workflow_id_workflow_id_fk", + "tableFrom": "workspace_fork_workflow_sync", + "tableTo": "workflow", + "columnsFrom": ["source_workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_fork_workflow_sync_target_workflow_id_workflow_id_fk": { + "name": "workspace_fork_workflow_sync_target_workflow_id_workflow_id_fk", + "tableFrom": "workspace_fork_workflow_sync", + "tableTo": "workflow", + "columnsFrom": ["target_workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_operation_receipt": { + "name": "workspace_operation_receipt", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "report": { + "name": "report", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_operation_receipt_request_unique": { + "name": "workspace_operation_receipt_request_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_operation_receipt_workspace_created_idx": { + "name": "workspace_operation_receipt_workspace_created_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_operation_receipt_workspace_id_workspace_id_fk": { + "name": "workspace_operation_receipt_workspace_id_workspace_id_fk", + "tableFrom": "workspace_operation_receipt", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_sandbox": { + "name": "workspace_sandbox", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "language": { + "name": "language", + "type": "sandbox_language", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "dependencies": { + "name": "dependencies", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "cli_tools": { + "name": "cli_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "system_packages": { + "name": "system_packages", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "spec_hash": { + "name": "spec_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_sandbox_workspace_name_unique": { + "name": "workspace_sandbox_workspace_name_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_sandbox_workspace_idx": { + "name": "workspace_sandbox_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_sandbox_spec_hash_idx": { + "name": "workspace_sandbox_spec_hash_idx", + "columns": [ + { + "expression": "spec_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_sandbox_workspace_id_workspace_id_fk": { + "name": "workspace_sandbox_workspace_id_workspace_id_fk", + "tableFrom": "workspace_sandbox", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_sandbox_created_by_user_id_fk": { + "name": "workspace_sandbox_created_by_user_id_fk", + "tableFrom": "workspace_sandbox", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_visit": { + "name": "workspace_visit", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visited_at": { + "name": "visited_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_visit_workspace_idx": { + "name": "workspace_visit_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_visit_user_id_user_id_fk": { + "name": "workspace_visit_user_id_user_id_fk", + "tableFrom": "workspace_visit", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_visit_workspace_id_workspace_id_fk": { + "name": "workspace_visit_workspace_id_workspace_id_fk", + "tableFrom": "workspace_visit", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "workspace_visit_user_id_workspace_id_pk": { + "name": "workspace_visit_user_id_workspace_id_pk", + "columns": ["user_id", "workspace_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.academy_cert_status": { + "name": "academy_cert_status", + "schema": "public", + "values": ["active", "revoked", "expired"] + }, + "public.background_work_kind": { + "name": "background_work_kind", + "schema": "public", + "values": ["deployment_side_effects", "fork_content_copy", "fork_sync", "fork_rollback"] + }, + "public.background_work_status_value": { + "name": "background_work_status_value", + "schema": "public", + "values": ["pending", "processing", "completed", "completed_with_warnings", "failed"] + }, + "public.billing_blocked_reason": { + "name": "billing_blocked_reason", + "schema": "public", + "values": ["payment_failed", "dispute"] + }, + "public.billing_entity_type": { + "name": "billing_entity_type", + "schema": "public", + "values": ["user", "organization"] + }, + "public.chat_type": { + "name": "chat_type", + "schema": "public", + "values": ["mothership", "copilot"] + }, + "public.copilot_async_tool_status": { + "name": "copilot_async_tool_status", + "schema": "public", + "values": ["pending", "running", "completed", "failed", "cancelled", "delivered"] + }, + "public.copilot_run_status": { + "name": "copilot_run_status", + "schema": "public", + "values": ["active", "paused_waiting_for_tool", "resuming", "complete", "error", "cancelled"] + }, + "public.copilot_tool_permission_decision": { + "name": "copilot_tool_permission_decision", + "schema": "public", + "values": ["allow", "allow_chat", "always_allow", "skip"] + }, + "public.credential_group_enrollment_status": { + "name": "credential_group_enrollment_status", + "schema": "public", + "values": ["invited", "delivery_failed", "in_progress", "completed", "revoked"] + }, + "public.credential_group_status": { + "name": "credential_group_status", + "schema": "public", + "values": ["active", "disabled"] + }, + "public.credential_member_role": { + "name": "credential_member_role", + "schema": "public", + "values": ["admin", "member"] + }, + "public.credential_member_status": { + "name": "credential_member_status", + "schema": "public", + "values": ["active", "pending", "revoked"] + }, + "public.credential_type": { + "name": "credential_type", + "schema": "public", + "values": [ + "oauth", + "managed_oauth", + "managed_mcp", + "env_workspace", + "env_personal", + "service_account", + "personal_token" + ] + }, + "public.data_drain_cadence": { + "name": "data_drain_cadence", + "schema": "public", + "values": ["hourly", "daily"] + }, + "public.data_drain_destination": { + "name": "data_drain_destination", + "schema": "public", + "values": ["s3", "gcs", "azure_blob", "datadog", "bigquery", "snowflake", "webhook"] + }, + "public.data_drain_run_status": { + "name": "data_drain_run_status", + "schema": "public", + "values": ["running", "success", "failed"] + }, + "public.data_drain_run_trigger": { + "name": "data_drain_run_trigger", + "schema": "public", + "values": ["cron", "manual"] + }, + "public.data_drain_source": { + "name": "data_drain_source", + "schema": "public", + "values": ["workflow_logs", "job_logs", "audit_logs", "copilot_chats", "copilot_runs"] + }, + "public.execution_large_value_reference_source": { + "name": "execution_large_value_reference_source", + "schema": "public", + "values": ["execution_log", "paused_snapshot"] + }, + "public.folder_resource_type": { + "name": "folder_resource_type", + "schema": "public", + "values": ["workflow", "file", "knowledge_base", "table"] + }, + "public.invitation_kind": { + "name": "invitation_kind", + "schema": "public", + "values": ["organization", "workspace"] + }, + "public.invitation_membership_intent": { + "name": "invitation_membership_intent", + "schema": "public", + "values": ["internal", "external"] + }, + "public.invitation_status": { + "name": "invitation_status", + "schema": "public", + "values": ["pending", "accepted", "rejected", "cancelled", "expired"] + }, + "public.managed_oauth_credential_status": { + "name": "managed_oauth_credential_status", + "schema": "public", + "values": ["active", "needs_reauth", "revoked"] + }, + "public.permission_type": { + "name": "permission_type", + "schema": "public", + "values": ["admin", "write", "read"] + }, + "public.sandbox_image_status": { + "name": "sandbox_image_status", + "schema": "public", + "values": ["pending", "building", "ready", "failed"] + }, + "public.sandbox_language": { + "name": "sandbox_language", + "schema": "public", + "values": ["javascript", "python"] + }, + "public.secret_usage_scope": { + "name": "secret_usage_scope", + "schema": "public", + "values": ["workspace", "personal"] + }, + "public.secret_usage_source": { + "name": "secret_usage_source", + "schema": "public", + "values": ["workflow", "copilot", "mcp"] + }, + "public.upload_session_method": { + "name": "upload_session_method", + "schema": "public", + "values": ["put", "multipart"] + }, + "public.upload_session_provider": { + "name": "upload_session_provider", + "schema": "public", + "values": ["local", "s3", "blob", "gcs"] + }, + "public.upload_session_purpose": { + "name": "upload_session_purpose", + "schema": "public", + "values": [ + "workspace_file", + "table_import", + "knowledge_document", + "profile_picture", + "workspace_logo", + "organization_logo", + "mothership_attachment", + "execution_attachment" + ] + }, + "public.upload_session_status": { + "name": "upload_session_status", + "schema": "public", + "values": [ + "uploading", + "completing", + "finalizing", + "completed", + "aborting", + "aborted", + "failed", + "expired" + ] + }, + "public.usage_log_category": { + "name": "usage_log_category", + "schema": "public", + "values": ["model", "fixed", "tool", "model_unbilled"] + }, + "public.usage_log_source": { + "name": "usage_log_source", + "schema": "public", + "values": [ + "workflow", + "wand", + "copilot", + "workspace-chat", + "mcp_copilot", + "mothership_block", + "knowledge-base", + "voice-input", + "enrichment", + "voice-output", + "api-tool", + "workflow-test" + ] + }, + "public.workspace_file_search_index_status": { + "name": "workspace_file_search_index_status", + "schema": "public", + "values": ["pending", "ready", "skipped", "failed"] + }, + "public.workspace_file_version_source": { + "name": "workspace_file_version_source", + "schema": "public", + "values": ["upload", "user", "api", "copilot", "workflow", "collab", "revert", "unknown"] + }, + "public.workspace_fork_promote_direction": { + "name": "workspace_fork_promote_direction", + "schema": "public", + "values": ["push", "pull"] + }, + "public.workspace_fork_resource_type": { + "name": "workspace_fork_resource_type", + "schema": "public", + "values": [ + "workflow", + "oauth_credential", + "service_account_credential", + "env_var", + "table", + "knowledge_base", + "knowledge_document", + "file", + "file_folder", + "mcp_server", + "workflow_mcp_server", + "custom_block", + "custom_tool", + "skill", + "sandbox" + ] + }, + "public.workspace_mode": { + "name": "workspace_mode", + "schema": "public", + "values": ["personal", "organization", "grandfathered_shared"] + } + }, + "schemas": {}, + "sequences": { + "public.copilot_async_tool_calls_persist_seq": { + "name": "copilot_async_tool_calls_persist_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json index 5c098ad6be0..ef5d135e1a9 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -2815,6 +2815,13 @@ "when": 1791496539437, "tag": "0402_table_row_change_triggers", "breakpoints": true + }, + { + "idx": 403, + "version": "7", + "when": 1791499299484, + "tag": "0403_workflow_tests", + "breakpoints": true } ] } diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 611943014f2..a2f36b06431 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2494,6 +2494,97 @@ export const dashboard = pgTable( }) ) +/** + * A workflow test file: one concern, read as `tests/.test.js`. `tests create` sets the + * name, title, and description; the source is a workspace file with `context = 'test'`, so it + * keeps versions and Sim's file edits, and `cases` is what its last accepted write declared. + */ +export const workflowTest = pgTable( + 'workflow_test', + { + id: text('id').primaryKey(), + workspaceId: text('workspace_id') + .notNull() + .references(() => workspace.id, { onDelete: 'cascade' }), + name: text('name').notNull(), + title: text('title').notNull(), + description: text('description'), + bodyFileId: text('body_file_id') + .notNull() + .references(() => workspaceFiles.id), + /** Every `describe > it` path in the file with its mode (`run`, `only`, `skip`). */ + cases: jsonb('cases').notNull().default('[]'), + /** sha256 of the source `cases` was read from; a run reporting another hash is stale. */ + sourceHash: text('source_hash').notNull(), + createdByUserId: text('created_by_user_id').references(() => user.id, { + onDelete: 'set null', + }), + deletedAt: timestamp('deleted_at'), + createdAt: timestamp('created_at').notNull().defaultNow(), + updatedAt: timestamp('updated_at').notNull().defaultNow(), + }, + (table) => ({ + workspaceNameUnique: uniqueIndex('workflow_test_workspace_name_unique') + .on(table.workspaceId, table.name) + .where(sql`${table.deletedAt} IS NULL`), + bodyFileUnique: uniqueIndex('workflow_test_body_file_unique').on(table.bodyFileId), + }) +) + +/** One run of a test file against the draft or deployed workflows. */ +export const workflowTestRun = pgTable( + 'workflow_test_run', + { + id: text('id').primaryKey(), + testId: text('test_id') + .notNull() + .references(() => workflowTest.id, { onDelete: 'cascade' }), + workspaceId: text('workspace_id') + .notNull() + .references(() => workspace.id, { onDelete: 'cascade' }), + version: text('version').notNull(), + status: text('status').notNull().default('running'), + passed: integer('passed').notNull().default(0), + failed: integer('failed').notNull().default(0), + skipped: integer('skipped').notNull().default(0), + /** The sandbox report: every case's checks, judge reasons, logs, and executions. */ + report: jsonb('report'), + /** Why the file produced no report: it did not load, or the run itself failed. */ + error: text('error'), + /** sha256 of the source this run executed; null when it failed before reading the file. */ + sourceHash: text('source_hash'), + /** `{ workflowId, deploymentVersionId }` for each workflow the run executed; null draft ids. */ + ranAgainst: jsonb('ran_against'), + /** Each case's status (`running`, `pass`, `fail`, `skip`) by `describe > it` path, as it runs. */ + progress: jsonb('progress'), + triggeredByActor: jsonb('triggered_by_actor').notNull(), + triggeredByUserId: text('triggered_by_user_id').references(() => user.id, { + onDelete: 'set null', + }), + startedAt: timestamp('started_at').notNull().defaultNow(), + completedAt: timestamp('completed_at'), + }, + (table) => ({ + testVersionStartedIdx: index('workflow_test_run_test_version_started_idx').on( + table.testId, + table.version, + table.startedAt + ), + versionCheck: check( + 'workflow_test_run_version_check', + sql`${table.version} IN ('draft', 'deployed')` + ), + statusCheck: check( + 'workflow_test_run_status_check', + sql`${table.status} IN ('running', 'passed', 'failed', 'error')` + ), + completedCheck: check( + 'workflow_test_run_completed_check', + sql`(${table.status} = 'running') = (${table.completedAt} IS NULL)` + ), + }) +) + export const workspaceFiles = pgTable( 'workspace_files', { @@ -5340,6 +5431,7 @@ export const usageLogSourceEnum = pgEnum('usage_log_source', [ 'enrichment', 'voice-output', 'api-tool', + 'workflow-test', ]) /** Content-free organization Search activity, independent of billable model usage. */ diff --git a/packages/testing/src/mocks/schema-tables.generated.ts b/packages/testing/src/mocks/schema-tables.generated.ts index 1c89248bda5..6c9d6a7b6da 100644 --- a/packages/testing/src/mocks/schema-tables.generated.ts +++ b/packages/testing/src/mocks/schema-tables.generated.ts @@ -600,6 +600,39 @@ export const GENERATED_SCHEMA_TABLES = { 'createdAt', 'updatedAt', ], + workflowTest: [ + 'id', + 'workspaceId', + 'name', + 'title', + 'description', + 'bodyFileId', + 'cases', + 'sourceHash', + 'createdByUserId', + 'deletedAt', + 'createdAt', + 'updatedAt', + ], + workflowTestRun: [ + 'id', + 'testId', + 'workspaceId', + 'version', + 'status', + 'passed', + 'failed', + 'skipped', + 'report', + 'error', + 'sourceHash', + 'ranAgainst', + 'progress', + 'triggeredByActor', + 'triggeredByUserId', + 'startedAt', + 'completedAt', + ], workspaceFiles: [ 'id', 'key', @@ -2150,6 +2183,7 @@ export const GENERATED_SCHEMA_ENUMS = { 'enrichment', 'voice-output', 'api-tool', + 'workflow-test', ], credentialTypeEnum: [ 'oauth', diff --git a/scripts/check-tool-registry-boundary.baseline.json b/scripts/check-tool-registry-boundary.baseline.json index 869afd33ed9..7b11b3de840 100644 --- a/scripts/check-tool-registry-boundary.baseline.json +++ b/scripts/check-tool-registry-boundary.baseline.json @@ -6,66 +6,66 @@ }, "entries": { "app/api/v2/blocks/[blockId]/route.ts": { - "modules": 1751, + "modules": 1759, "gateways": { "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/api/server/routes/index.ts": 519, - "apps/sim/lib/api/server/routes/internal-json-route.ts": 463, - "apps/sim/lib/auth/index.ts": 449, + "apps/sim/lib/api/server/routes/index.ts": 527, + "apps/sim/lib/api/server/routes/internal-json-route.ts": 471, + "apps/sim/lib/auth/index.ts": 457, "apps/sim/blocks/registry.ts": 370, "apps/sim/lib/webhooks/providers/index.ts": 124, "apps/sim/lib/webhooks/providers/registry.ts": 122 } }, "app/api/v2/blocks/route.ts": { - "modules": 1748, + "modules": 1756, "gateways": { "apps/sim/blocks/registry.ts": 910, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/api/server/routes/index.ts": 512, - "apps/sim/lib/api/server/routes/internal-json-route.ts": 464, - "apps/sim/lib/auth/index.ts": 450, + "apps/sim/lib/api/server/routes/index.ts": 520, + "apps/sim/lib/api/server/routes/internal-json-route.ts": 472, + "apps/sim/lib/auth/index.ts": 458, "apps/sim/lib/webhooks/providers/index.ts": 125, "apps/sim/lib/webhooks/providers/registry.ts": 122 } }, "app/api/v2/connector-types/route.ts": { - "modules": 1817, + "modules": 1825, "gateways": { "apps/sim/blocks/registry.ts": 911, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/api/server/routes/index.ts": 510, - "apps/sim/lib/api/server/routes/internal-json-route.ts": 462, - "apps/sim/lib/auth/index.ts": 448, + "apps/sim/lib/api/server/routes/index.ts": 518, + "apps/sim/lib/api/server/routes/internal-json-route.ts": 470, + "apps/sim/lib/auth/index.ts": 456, "apps/sim/lib/webhooks/providers/index.ts": 125, "apps/sim/lib/webhooks/providers/registry.ts": 122 } }, "app/api/v2/tools/[toolId]/route.ts": { - "modules": 1746, + "modules": 1754, "gateways": { "apps/sim/blocks/registry.ts": 910, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/api/server/routes/index.ts": 520, - "apps/sim/lib/api/server/routes/internal-json-route.ts": 464, - "apps/sim/lib/auth/index.ts": 450, + "apps/sim/lib/api/server/routes/index.ts": 528, + "apps/sim/lib/api/server/routes/internal-json-route.ts": 472, + "apps/sim/lib/auth/index.ts": 458, "apps/sim/lib/webhooks/providers/index.ts": 125, "apps/sim/lib/webhooks/providers/registry.ts": 122 } }, "app/api/v2/tools/route.ts": { - "modules": 1747, + "modules": 1755, "gateways": { "apps/sim/blocks/registry.ts": 910, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/api/server/routes/index.ts": 512, - "apps/sim/lib/api/server/routes/internal-json-route.ts": 464, - "apps/sim/lib/auth/index.ts": 450, + "apps/sim/lib/api/server/routes/index.ts": 520, + "apps/sim/lib/api/server/routes/internal-json-route.ts": 472, + "apps/sim/lib/auth/index.ts": 458, "apps/sim/lib/webhooks/providers/index.ts": 125, "apps/sim/lib/webhooks/providers/registry.ts": 122 } @@ -87,13 +87,13 @@ "gateways": {} }, "app/workspace/[workspaceId]/chat/[chatId]/page.tsx": { - "modules": 3417, + "modules": 3433, "gateways": { - "apps/sim/app/workspace/[workspaceId]/home/home.tsx": 1704, - "apps/sim/app/workspace/[workspaceId]/home/components/chat-resource-panel.tsx": 1055, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx": 1051, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/index.ts": 718, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/index.ts": 715, + "apps/sim/app/workspace/[workspaceId]/home/home.tsx": 1712, + "apps/sim/app/workspace/[workspaceId]/home/components/chat-resource-panel.tsx": 1060, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx": 1056, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/index.ts": 723, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/index.ts": 720, "apps/sim/triggers/registry.ts": 534, "apps/sim/blocks/registry.ts": 339, "apps/sim/lib/auth/index.ts": 290 @@ -129,7 +129,7 @@ "gateways": {} }, "app/workspace/[workspaceId]/files/[fileId]/page.tsx": { - "modules": 2366, + "modules": 2374, "gateways": { "apps/sim/triggers/registry.ts": 534, "apps/sim/app/workspace/[workspaceId]/files/files.tsx": 513, @@ -157,7 +157,7 @@ "gateways": {} }, "app/workspace/[workspaceId]/files/page.tsx": { - "modules": 2365, + "modules": 2373, "gateways": { "apps/sim/triggers/registry.ts": 534, "apps/sim/app/workspace/[workspaceId]/files/files.tsx": 514, @@ -178,13 +178,13 @@ "gateways": {} }, "app/workspace/[workspaceId]/home/page.tsx": { - "modules": 3417, + "modules": 3433, "gateways": { - "apps/sim/app/workspace/[workspaceId]/home/home.tsx": 1704, - "apps/sim/app/workspace/[workspaceId]/home/components/chat-resource-panel.tsx": 1055, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx": 1051, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/index.ts": 718, - "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/index.ts": 715, + "apps/sim/app/workspace/[workspaceId]/home/home.tsx": 1712, + "apps/sim/app/workspace/[workspaceId]/home/components/chat-resource-panel.tsx": 1060, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/mothership-view.tsx": 1056, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/index.ts": 723, + "apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/index.ts": 720, "apps/sim/triggers/registry.ts": 534, "apps/sim/blocks/registry.ts": 339, "apps/sim/lib/auth/index.ts": 290 @@ -279,7 +279,7 @@ "gateways": {} }, "app/workspace/[workspaceId]/knowledge/page.tsx": { - "modules": 2528, + "modules": 2536, "gateways": { "apps/sim/triggers/registry.ts": 534, "apps/sim/app/workspace/[workspaceId]/knowledge/prefetch.ts": 441, @@ -292,10 +292,10 @@ } }, "app/workspace/[workspaceId]/layout.tsx": { - "modules": 2381, + "modules": 2390, "gateways": { "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/auth/index.ts": 431, + "apps/sim/lib/auth/index.ts": 437, "apps/sim/blocks/registry.ts": 357, "apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx": 279, "apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/index.ts": 151, @@ -342,12 +342,12 @@ "gateways": {} }, "app/workspace/[workspaceId]/settings/[section]/page.tsx": { - "modules": 2911, + "modules": 2922, "gateways": { - "apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx": 1149, - "apps/sim/app/workspace/[workspaceId]/settings/section-warmers.ts": 958, + "apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx": 1152, + "apps/sim/app/workspace/[workspaceId]/settings/section-warmers.ts": 960, "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/auth/index.ts": 413, + "apps/sim/lib/auth/index.ts": 420, "apps/sim/ee/workspace-forking/components/forks.tsx": 405, "apps/sim/ee/workspace-forking/components/fork-sync-detail-view/fork-sync-detail-view.tsx": 397, "apps/sim/ee/workspace-forking/components/fork-sync/fork-sync-view.tsx": 389, @@ -363,16 +363,16 @@ "gateways": {} }, "app/workspace/[workspaceId]/settings/billing/credit-usage/page.tsx": { - "modules": 1700, + "modules": 1708, "gateways": { - "apps/sim/lib/auth/index.ts": 1544, + "apps/sim/lib/auth/index.ts": 1552, "apps/sim/blocks/registry.ts": 914, "apps/sim/blocks/registry-maps.ts": 911, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, "apps/sim/lib/webhooks/providers/index.ts": 127, "apps/sim/lib/webhooks/providers/registry.ts": 124, - "apps/sim/lib/uploads/utils/file-utils.server.ts": 54 + "apps/sim/lib/uploads/utils/file-utils.server.ts": 59 } }, "app/workspace/[workspaceId]/settings/error.tsx": { @@ -380,9 +380,9 @@ "gateways": {} }, "app/workspace/[workspaceId]/settings/layout.tsx": { - "modules": 2269, + "modules": 2273, "gateways": { - "apps/sim/app/workspace/[workspaceId]/settings/section-warmers.ts": 2232, + "apps/sim/app/workspace/[workspaceId]/settings/section-warmers.ts": 2236, "apps/sim/triggers/registry.ts": 574, "apps/sim/ee/workspace-forking/components/forks.tsx": 409, "apps/sim/ee/workspace-forking/components/fork-sync-detail-view/fork-sync-detail-view.tsx": 401, @@ -431,16 +431,16 @@ "gateways": {} }, "app/workspace/[workspaceId]/settings/usage/events/page.tsx": { - "modules": 1707, + "modules": 1715, "gateways": { - "apps/sim/lib/auth/index.ts": 1544, + "apps/sim/lib/auth/index.ts": 1552, "apps/sim/blocks/registry.ts": 914, "apps/sim/blocks/registry-maps.ts": 911, "apps/sim/triggers/index.ts": 536, "apps/sim/triggers/registry.ts": 534, "apps/sim/lib/webhooks/providers/index.ts": 127, "apps/sim/lib/webhooks/providers/registry.ts": 124, - "apps/sim/lib/uploads/utils/file-utils.server.ts": 54 + "apps/sim/lib/uploads/utils/file-utils.server.ts": 59 } }, "app/workspace/[workspaceId]/skills/[skillId]/page.tsx": { @@ -516,16 +516,63 @@ "gateways": {} }, "app/workspace/[workspaceId]/tables/page.tsx": { - "modules": 2068, + "modules": 2077, "gateways": { "apps/sim/triggers/registry.ts": 534, - "apps/sim/lib/auth/index.ts": 459, + "apps/sim/lib/auth/index.ts": 467, "apps/sim/blocks/registry.ts": 365, "apps/sim/app/workspace/[workspaceId]/tables/tables.tsx": 205, "apps/sim/lib/webhooks/providers/index.ts": 124, "apps/sim/lib/webhooks/providers/registry.ts": 122, - "apps/sim/app/workspace/[workspaceId]/tables/prefetch.ts": 102, - "apps/sim/app/workspace/[workspaceId]/lib/authorize-resource-prefetch.ts": 54 + "apps/sim/app/workspace/[workspaceId]/tables/prefetch.ts": 103, + "apps/sim/lib/uploads/utils/file-utils.server.ts": 57 + } + }, + "app/workspace/[workspaceId]/tests/[name]/loading.tsx": { + "modules": 15, + "gateways": {} + }, + "app/workspace/[workspaceId]/tests/[name]/page.tsx": { + "modules": 1921, + "gateways": { + "apps/sim/app/workspace/[workspaceId]/tests/components/index.ts": 1920, + "apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx": 1917, + "apps/sim/triggers/registry.ts": 574, + "apps/sim/blocks/registry.ts": 357, + "apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx": 337, + "apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/index.ts": 289, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/index.ts": 281, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx": 207 + } + }, + "app/workspace/[workspaceId]/tests/layout.tsx": { + "modules": 1921, + "gateways": { + "apps/sim/app/workspace/[workspaceId]/tests/components/index.ts": 1920, + "apps/sim/app/workspace/[workspaceId]/tests/components/test-detail.tsx": 1917, + "apps/sim/triggers/registry.ts": 574, + "apps/sim/blocks/registry.ts": 357, + "apps/sim/app/workspace/[workspaceId]/tests/components/test-dashboard.tsx": 337, + "apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/index.ts": 289, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/index.ts": 281, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx": 207 + } + }, + "app/workspace/[workspaceId]/tests/loading.tsx": { + "modules": 15, + "gateways": {} + }, + "app/workspace/[workspaceId]/tests/page.tsx": { + "modules": 1247, + "gateways": { + "apps/sim/app/workspace/[workspaceId]/tests/tests.tsx": 1231, + "apps/sim/app/workspace/[workspaceId]/providers/workspace-permissions-provider.tsx": 1090, + "apps/sim/triggers/registry.ts": 574, + "apps/sim/blocks/registry.ts": 383, + "apps/sim/blocks/registry-maps.ts": 380, + "apps/sim/lib/api/contracts/index.ts": 60, + "apps/sim/triggers/plane/index.ts": 46, + "apps/sim/stores/workflows/registry/store.ts": 39 } }, "app/workspace/[workspaceId]/upgrade/page.tsx": { @@ -544,11 +591,11 @@ "gateways": {} }, "app/workspace/[workspaceId]/w/[workflowId]/page.tsx": { - "modules": 2402, + "modules": 2406, "gateways": { - "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx": 2401, - "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/index.ts": 665, - "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/index.ts": 617, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx": 2405, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/index.ts": 668, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/index.ts": 620, "apps/sim/triggers/registry.ts": 574, "apps/sim/blocks/registry.ts": 357, "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/index.ts": 290, @@ -557,10 +604,10 @@ } }, "app/workspace/[workspaceId]/w/page.tsx": { - "modules": 2383, + "modules": 2387, "gateways": { - "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/index.ts": 1111, - "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/index.ts": 833, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/index.ts": 1115, + "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/index.ts": 836, "apps/sim/triggers/registry.ts": 574, "apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/index.ts": 436, "apps/sim/blocks/registry.ts": 357, @@ -583,16 +630,16 @@ } }, "app/workspace/page.tsx": { - "modules": 1199, + "modules": 1200, "gateways": { - "apps/sim/lib/auth/stale-session-recovery.ts": 1079, + "apps/sim/lib/auth/stale-session-recovery.ts": 1080, + "apps/sim/stores/reset-all-stores.ts": 1077, "apps/sim/triggers/index.ts": 576, "apps/sim/triggers/registry.ts": 574, "apps/sim/blocks/registry.ts": 384, "apps/sim/blocks/registry-maps.ts": 381, "apps/sim/lib/api/contracts/index.ts": 55, - "apps/sim/triggers/plane/index.ts": 46, - "apps/sim/stores/workflows/registry/store.ts": 45 + "apps/sim/triggers/plane/index.ts": 46 } }, "lib/catalog/projection/block-detail.ts": { diff --git a/scripts/check-unused-exports.baseline.json b/scripts/check-unused-exports.baseline.json index 255f6896674..684f5cac12f 100644 --- a/scripts/check-unused-exports.baseline.json +++ b/scripts/check-unused-exports.baseline.json @@ -4525,7 +4525,6 @@ "apps/sim/lib/execution/remote-sandbox/workspace-sandboxes.ts#isSandboxNameTaken", "apps/sim/lib/execution/remote-sandbox/workspace-sandboxes.ts#isWorkspaceSandboxNameConflictError", "apps/sim/lib/execution/remote-sandbox/workspace-sandboxes.ts#scheduleSandboxBuild", - "apps/sim/lib/execution/sandbox/run-task.ts#SandboxUserCodeError", "apps/sim/lib/file-parsers/errors.ts#FILE_PARSER_ERROR_CODES", "apps/sim/lib/file-parsers/index.ts#parseFile", "apps/sim/lib/file-parsers/json-parser.ts#stripJsonComments",