From 00a83d0289d84f2e7957980e7efd302e220178d6 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 00:24:02 -0700 Subject: [PATCH 001/113] feat(projects): add project identity and lifecycle foundation --- apps/docs/openapi-v2-resources.json | 66 +- apps/sim/app/api/projects/[id]/route.ts | 45 + .../by-workspace/[workspaceId]/route.ts | 18 + apps/sim/app/api/projects/route.ts | 18 + apps/sim/app/api/workflows/route.test.ts | 3 + .../components/group-detail.tsx | 11 + .../components/project-issue-restrictions.tsx | 60 + .../lib/copy/copy-workflows.test.ts | 26 +- .../lib/copy/copy-workflows.ts | 2 + .../workspace-forking/lib/create-fork.test.ts | 16 +- .../ee/workspace-forking/lib/create-fork.ts | 13 +- .../lib/lineage/unlink.test.ts | 23 +- .../workspace-forking/lib/lineage/unlink.ts | 12 +- apps/sim/hooks/queries/projects.ts | 25 + apps/sim/lib/api/contracts/projects.ts | 86 + .../billing/organizations/lock-order.test.ts | 1 + .../lib/billing/organizations/membership.ts | 21 + .../sim/lib/permission-groups/capabilities.ts | 9 + apps/sim/lib/permission-groups/fields.test.ts | 2 + apps/sim/lib/permission-groups/fields.ts | 5 + .../lib/permission-groups/group-manager.ts | 36 +- .../__integration__/foundation.integration.ts | 619 + apps/sim/lib/projects/account-deletion.ts | 83 + .../lib/projects/application/authorization.ts | 128 + apps/sim/lib/projects/application/index.ts | 9 + .../lib/projects/application/operations.ts | 52 + .../sim/lib/projects/application/use-cases.ts | 204 + apps/sim/lib/projects/lifecycle.ts | 66 + apps/sim/lib/projects/membership.ts | 238 + apps/sim/lib/users/account-deletion.ts | 2 + apps/sim/lib/workflows/lifecycle.ts | 216 +- .../orchestration/workflow-lifecycle.test.ts | 5 + .../workflows/persistence/duplicate.test.ts | 3 + .../workflows/persistence/new-workflow-row.ts | 4 +- .../__integration__/fork-sync.integration.ts | 24 + apps/sim/lib/workspaces/active-workspace.ts | 19 + apps/sim/lib/workspaces/admin-move.ts | 3 + apps/sim/lib/workspaces/create.test.ts | 3 + apps/sim/lib/workspaces/create.ts | 8 + apps/sim/lib/workspaces/lifecycle.test.ts | 99 - apps/sim/lib/workspaces/lifecycle.ts | 228 +- .../organization-workspaces.integration.ts | 1 + .../lib/workspaces/organization-workspaces.ts | 3 + docs/plans/project-backfill-runbook.md | 68 + docs/plans/project-entity-foundation.md | 311 + knip.jsonc | 2 + packages/audit/src/types.ts | 3 + .../db/migrations/0393_project_foundation.sql | 25 + .../db/migrations/meta/0393_snapshot.json | 29715 ++++++++++++++++ packages/db/migrations/meta/_journal.json | 7 + packages/db/package.json | 4 + packages/db/project-backfill.ts | 471 + packages/db/schema.ts | 49 + packages/db/scripts/backfill-projects.ts | 168 + .../scripts/project-backfill.integration.ts | 396 + packages/sim-cli/src/generated/v2-api.ts | 9 + .../src/mocks/schema-tables.generated.ts | 2 + 57 files changed, 33404 insertions(+), 341 deletions(-) create mode 100644 apps/sim/app/api/projects/[id]/route.ts create mode 100644 apps/sim/app/api/projects/by-workspace/[workspaceId]/route.ts create mode 100644 apps/sim/app/api/projects/route.ts create mode 100644 apps/sim/ee/access-control/components/project-issue-restrictions.tsx create mode 100644 apps/sim/hooks/queries/projects.ts create mode 100644 apps/sim/lib/api/contracts/projects.ts create mode 100644 apps/sim/lib/projects/__integration__/foundation.integration.ts create mode 100644 apps/sim/lib/projects/account-deletion.ts create mode 100644 apps/sim/lib/projects/application/authorization.ts create mode 100644 apps/sim/lib/projects/application/index.ts create mode 100644 apps/sim/lib/projects/application/operations.ts create mode 100644 apps/sim/lib/projects/application/use-cases.ts create mode 100644 apps/sim/lib/projects/lifecycle.ts create mode 100644 apps/sim/lib/projects/membership.ts create mode 100644 apps/sim/lib/workspaces/active-workspace.ts delete mode 100644 apps/sim/lib/workspaces/lifecycle.test.ts create mode 100644 docs/plans/project-backfill-runbook.md create mode 100644 docs/plans/project-entity-foundation.md create mode 100644 packages/db/migrations/0393_project_foundation.sql create mode 100644 packages/db/migrations/meta/0393_snapshot.json create mode 100644 packages/db/project-backfill.ts create mode 100644 packages/db/scripts/backfill-projects.ts create mode 100644 packages/db/scripts/project-backfill.integration.ts diff --git a/apps/docs/openapi-v2-resources.json b/apps/docs/openapi-v2-resources.json index e9ec3dde864..0be846c9c8a 100644 --- a/apps/docs/openapi-v2-resources.json +++ b/apps/docs/openapi-v2-resources.json @@ -15996,6 +15996,16 @@ "disableKnowledgeBaseExport": { "type": "boolean", "description": "Prevent downloading a whole knowledge base as an archive." + }, + "deniedPartialAccessProjectIssues": { + "default": [], + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 255 + }, + "description": "Issues in the listed Projects are unavailable to teammates without access to every active environment." } }, "required": [ @@ -16040,7 +16050,8 @@ "disableToolAutoApproval", "hideSandboxesTab", "disableOAuthAppAccess", - "disableKnowledgeBaseExport" + "disableKnowledgeBaseExport", + "deniedPartialAccessProjectIssues" ], "additionalProperties": false, "description": "Resolved restrictions. True disables a boolean capability; null allowlists permit every value and empty allowlists permit none." @@ -16170,7 +16181,8 @@ "disableToolAutoApproval": false, "hideSandboxesTab": false, "disableOAuthAppAccess": false, - "disableKnowledgeBaseExport": false + "disableKnowledgeBaseExport": false, + "deniedPartialAccessProjectIssues": [] }, "isDefault": false, "membershipMode": "inherit", @@ -16245,7 +16257,8 @@ "disableToolAutoApproval": false, "hideSandboxesTab": false, "disableOAuthAppAccess": false, - "disableKnowledgeBaseExport": false + "disableKnowledgeBaseExport": false, + "deniedPartialAccessProjectIssues": [] }, "isDefault": false, "membershipMode": "inherit", @@ -16499,6 +16512,15 @@ "disableKnowledgeBaseExport": { "type": "boolean", "description": "Prevent downloading a whole knowledge base as an archive." + }, + "deniedPartialAccessProjectIssues": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 255 + }, + "description": "Issues in the listed Projects are unavailable to teammates without access to every active environment." } }, "additionalProperties": false, @@ -16590,7 +16612,8 @@ "disableToolAutoApproval": false, "hideSandboxesTab": false, "disableOAuthAppAccess": false, - "disableKnowledgeBaseExport": false + "disableKnowledgeBaseExport": false, + "deniedPartialAccessProjectIssues": [] }, "isDefault": false, "membershipMode": "inherit", @@ -16663,7 +16686,8 @@ "disableToolAutoApproval": false, "hideSandboxesTab": false, "disableOAuthAppAccess": false, - "disableKnowledgeBaseExport": false + "disableKnowledgeBaseExport": false, + "deniedPartialAccessProjectIssues": [] }, "isDefault": false, "membershipMode": "inherit", @@ -16924,6 +16948,15 @@ "disableKnowledgeBaseExport": { "type": "boolean", "description": "Prevent downloading a whole knowledge base as an archive." + }, + "deniedPartialAccessProjectIssues": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 255 + }, + "description": "Issues in the listed Projects are unavailable to teammates without access to every active environment." } }, "additionalProperties": false, @@ -18200,6 +18233,16 @@ "disableKnowledgeBaseExport": { "type": "boolean", "description": "Prevent downloading a whole knowledge base as an archive." + }, + "deniedPartialAccessProjectIssues": { + "default": [], + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 255 + }, + "description": "Issues in the listed Projects are unavailable to teammates without access to every active environment." } }, "required": [ @@ -18244,7 +18287,8 @@ "disableToolAutoApproval", "hideSandboxesTab", "disableOAuthAppAccess", - "disableKnowledgeBaseExport" + "disableKnowledgeBaseExport", + "deniedPartialAccessProjectIssues" ], "additionalProperties": false }, @@ -20729,7 +20773,7 @@ "description": "Access request being reviewed." }, "changes": { - "maxItems": 42, + "maxItems": 43, "type": "array", "items": { "type": "object", @@ -20778,7 +20822,8 @@ "disableToolAutoApproval", "hideSandboxesTab", "disableOAuthAppAccess", - "disableKnowledgeBaseExport" + "disableKnowledgeBaseExport", + "deniedPartialAccessProjectIssues" ], "description": "Permission restriction changed by approval." }, @@ -21290,7 +21335,7 @@ "description": "Access request being reviewed." }, "changes": { - "maxItems": 42, + "maxItems": 43, "type": "array", "items": { "type": "object", @@ -21339,7 +21384,8 @@ "disableToolAutoApproval", "hideSandboxesTab", "disableOAuthAppAccess", - "disableKnowledgeBaseExport" + "disableKnowledgeBaseExport", + "deniedPartialAccessProjectIssues" ], "description": "Permission restriction changed by approval." }, diff --git a/apps/sim/app/api/projects/[id]/route.ts b/apps/sim/app/api/projects/[id]/route.ts new file mode 100644 index 00000000000..d8e454db122 --- /dev/null +++ b/apps/sim/app/api/projects/[id]/route.ts @@ -0,0 +1,45 @@ +import { + archiveProjectContract, + getProjectContract, + renameProjectContract, +} from '@/lib/api/contracts/projects' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { + archiveProject, + getProject, + projectOperations, + renameProject, +} from '@/lib/projects/application' + +export const GET = defineInternalJsonRoute({ + contract: getProjectContract, + auth: internalSessionAuth, + operation: projectOperations.get, + rateLimit: internalRateLimits.user({ bucketName: 'projects.read' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params, query }) => ({ projectId: params.id, ...query }), + useCase: getProject, +}) +export const PATCH = defineInternalJsonRoute({ + contract: renameProjectContract, + auth: internalSessionAuth, + operation: projectOperations.rename, + rateLimit: internalRateLimits.user({ bucketName: 'projects.write' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params, body }) => ({ projectId: params.id, name: body.name }), + useCase: renameProject, +}) +export const DELETE = defineInternalJsonRoute({ + contract: archiveProjectContract, + auth: internalSessionAuth, + operation: projectOperations.archive, + rateLimit: internalRateLimits.user({ bucketName: 'projects.write' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params }) => ({ projectId: params.id }), + useCase: archiveProject, +}) diff --git a/apps/sim/app/api/projects/by-workspace/[workspaceId]/route.ts b/apps/sim/app/api/projects/by-workspace/[workspaceId]/route.ts new file mode 100644 index 00000000000..d04b76ed361 --- /dev/null +++ b/apps/sim/app/api/projects/by-workspace/[workspaceId]/route.ts @@ -0,0 +1,18 @@ +import { getWorkspaceProjectContract } from '@/lib/api/contracts/projects' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { getWorkspaceProject, projectOperations } from '@/lib/projects/application' + +export const GET = defineInternalJsonRoute({ + contract: getWorkspaceProjectContract, + auth: internalSessionAuth, + operation: projectOperations.get, + rateLimit: internalRateLimits.user({ bucketName: 'projects.read' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ params }) => params, + useCase: getWorkspaceProject, +}) diff --git a/apps/sim/app/api/projects/route.ts b/apps/sim/app/api/projects/route.ts new file mode 100644 index 00000000000..fe0aca010ca --- /dev/null +++ b/apps/sim/app/api/projects/route.ts @@ -0,0 +1,18 @@ +import { listProjectsContract } from '@/lib/api/contracts/projects' +import { + defineInternalJsonRoute, + internalOrchestrationErrorPolicy, + internalRateLimits, + internalSessionAuth, +} from '@/lib/api/server/routes' +import { listProjects, projectOperations } from '@/lib/projects/application' + +export const GET = defineInternalJsonRoute({ + contract: listProjectsContract, + auth: internalSessionAuth, + operation: projectOperations.list, + rateLimit: internalRateLimits.user({ bucketName: 'projects.read' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ query }) => query, + useCase: listProjects, +}) diff --git a/apps/sim/app/api/workflows/route.test.ts b/apps/sim/app/api/workflows/route.test.ts index 8e3e44b65b5..53ddf9390ee 100644 --- a/apps/sim/app/api/workflows/route.test.ts +++ b/apps/sim/app/api/workflows/route.test.ts @@ -45,6 +45,9 @@ describe('Workflows API Route - POST ordering', () => { beforeEach(() => { resetDbChainMock() + queueTableRows(schemaMock.workspace, [ + { archivedAt: null, forkSyncNewWorkflowsExcluded: false }, + ]) vi.stubGlobal('crypto', { randomUUID: vi.fn().mockReturnValue('workflow-new-id'), diff --git a/apps/sim/ee/access-control/components/group-detail.tsx b/apps/sim/ee/access-control/components/group-detail.tsx index 16e819069bd..166c83e3284 100644 --- a/apps/sim/ee/access-control/components/group-detail.tsx +++ b/apps/sim/ee/access-control/components/group-detail.tsx @@ -61,6 +61,7 @@ import { getAllBlocks } from '@/blocks' import { useCustomBlockOverlayVersion } from '@/blocks/custom/client-overlay' import type { BlockConfig } from '@/blocks/types' import { CONNECTOR_META_REGISTRY } from '@/connectors/registry' +import { ProjectIssueRestrictions } from '@/ee/access-control/components/project-issue-restrictions' import { WorkspaceSelect } from '@/ee/access-control/components/workspace-select' import { type PermissionGroup, @@ -1783,6 +1784,16 @@ export function GroupDetail({ {configTab === 'platform' && (
+ + setEditingConfig((previous) => ({ + ...previous, + deniedPartialAccessProjectIssues: value, + })) + } + />
void +} + +/** Project choices use the authorized inventory; policy remains enforced at the application boundary. */ +export function ProjectIssueRestrictions({ + organizationId, + value, + onChange, +}: ProjectIssueRestrictionsProps) { + const projects = useProjects(organizationId) + const selected = new Set(value) + return ( +
+

Restrict Issues for partial-access teammates

+

+ For selected Projects, teammates governed by this group need access to every active + environment to use Issues. +

+ {projects.isPending &&

Loading Projects…

} + {projects.error && ( +

{projects.error.message}

+ )} + {projects.data?.pages + .flatMap((page) => page.projects) + .map((project) => ( + + ))} + {projects.hasNextPage && ( + void projects.fetchNextPage()}> + Load more + + )} +
+ ) +} diff --git a/apps/sim/ee/workspace-forking/lib/copy/copy-workflows.test.ts b/apps/sim/ee/workspace-forking/lib/copy/copy-workflows.test.ts index 00052299e3e..8c118090240 100644 --- a/apps/sim/ee/workspace-forking/lib/copy/copy-workflows.test.ts +++ b/apps/sim/ee/workspace-forking/lib/copy/copy-workflows.test.ts @@ -1,9 +1,11 @@ +import { workspace } from '@sim/db/schema' import { createBlock } from '@sim/testing/factories' +import { dbChainMock, queueTableRows, resetDbChainMock } from '@sim/testing/mocks/database.mock' import { workflowsPersistenceUtilsMock, workflowsPersistenceUtilsMockFns, } from '@sim/testing/mocks/workflows-persistence-utils.mock' -import { describe, expect, it, vi } from 'vitest' +import { beforeEach, describe, expect, it, vi } from 'vitest' import type { DbOrTx } from '@/lib/db/types' import { MAX_FOLDERS_PER_WORKSPACE } from '@/lib/folders/constants' import { FolderCollectionFullError } from '@/lib/folders/errors' @@ -23,6 +25,11 @@ import { const mockSaveWorkflowToNormalizedTables = workflowsPersistenceUtilsMockFns.mockSaveWorkflowToNormalizedTables +beforeEach(() => { + resetDbChainMock() + queueTableRows(workspace, [{ archivedAt: null, forkSyncNewWorkflowsExcluded: false }]) +}) + describe('buildWorkflowNameRegistry', () => { it('excludes the workflow itself so a replace can keep its own name', () => { const reg = buildWorkflowNameRegistry([{ id: 'w1', folderId: 'f1', name: 'Onboarding' }]) @@ -294,7 +301,7 @@ describe('copyWorkflowStateIntoTarget source tool identities', () => { async () => { mockSaveWorkflowToNormalizedTables.mockResolvedValue({ success: true }) await copyWorkflowStateIntoTarget({ - tx: { insert: () => ({ values: () => Promise.resolve() }) } as unknown as DbOrTx, + tx: dbChainMock.db as unknown as DbOrTx, targetWorkflowId: 'wf-child', targetWorkspaceId: 'ws-child', userId: 'user', @@ -358,7 +365,7 @@ describe('copied MCP configuration normalization', () => { }, }) await copyWorkflowStateIntoTarget({ - tx: { insert: () => ({ values: () => Promise.resolve() }) } as unknown as DbOrTx, + tx: dbChainMock.db as unknown as DbOrTx, targetWorkflowId: 'wf-child', targetWorkspaceId: 'ws-target', userId: 'target-user', @@ -396,9 +403,7 @@ describe('copyWorkflowStateIntoTarget canonicalModes reindex propagation', () => async () => { mockSaveWorkflowToNormalizedTables.mockResolvedValue({ success: true }) const seenCanonicalModes: Array | undefined> = [] - const tx = { - insert: () => ({ values: () => Promise.resolve() }), - } as unknown as DbOrTx + const tx = dbChainMock.db as unknown as DbOrTx await copyWorkflowStateIntoTarget({ tx, @@ -484,11 +489,7 @@ describe('copyWorkflowStateIntoTarget webhook path pinning', () => { resolveBlockId: (_targetWorkflowId: string, sourceBlockId: string) => `tgt-${sourceBlockId}`, } - /** `replace` mode updates the existing target workflow row; stub just that chain. */ - const stubTx = () => - ({ - update: () => ({ set: () => ({ where: () => Promise.resolve() }) }), - }) as unknown as DbOrTx + const stubTx = () => dbChainMock.db as unknown as DbOrTx function writtenSubBlocks() { const state = mockSaveWorkflowToNormalizedTables.mock.calls.at(-1)?.[1] as { @@ -549,8 +550,7 @@ describe('copyWorkflowStateIntoTarget custom-block remap', () => { resolveBlockId: (_t: string, sourceBlockId: string) => `tgt-${sourceBlockId}`, } - const stubTx = () => - ({ update: () => ({ set: () => ({ where: () => Promise.resolve() }) }) }) as unknown as DbOrTx + const stubTx = () => dbChainMock.db as unknown as DbOrTx function writtenBlock() { const state = mockSaveWorkflowToNormalizedTables.mock.calls.at(-1)?.[1] as { diff --git a/apps/sim/ee/workspace-forking/lib/copy/copy-workflows.ts b/apps/sim/ee/workspace-forking/lib/copy/copy-workflows.ts index 847f2ec2984..21c9c21b83d 100644 --- a/apps/sim/ee/workspace-forking/lib/copy/copy-workflows.ts +++ b/apps/sim/ee/workspace-forking/lib/copy/copy-workflows.ts @@ -26,6 +26,7 @@ import { type SubBlockTransform, } from '@/lib/workflows/references/remap-references' import type { CanonicalModeOverrides } from '@/lib/workflows/subblocks/visibility' +import { lockActiveWorkspace } from '@/lib/workspaces/active-workspace' import { deriveForkBlockId, type ForkBlockIdResolver, @@ -502,6 +503,7 @@ export async function copyWorkflowStateIntoTarget( requestId = 'unknown', } = params + await lockActiveWorkspace(tx, targetWorkspaceId) const targetFolderId = sourceMeta.folderId ? (folderIdMap.get(sourceMeta.folderId) ?? null) : null const varIdMapping = new Map() diff --git a/apps/sim/ee/workspace-forking/lib/create-fork.test.ts b/apps/sim/ee/workspace-forking/lib/create-fork.test.ts index 648815617f7..ff7d504868a 100644 --- a/apps/sim/ee/workspace-forking/lib/create-fork.test.ts +++ b/apps/sim/ee/workspace-forking/lib/create-fork.test.ts @@ -1,4 +1,4 @@ -import { workspace } from '@sim/db/schema' +import { projectWorkspace, workspace } from '@sim/db/schema' import { dbChainMockFns, queueTableRows, resetDbChainMock } from '@sim/testing' import { workflowsPersistenceUtilsMock } from '@sim/testing/mocks/workflows-persistence-utils.mock' import { @@ -140,12 +140,17 @@ function forkParams(selection?: { describe('createFork storage headroom gate', () => { beforeEach(() => { resetDbChainMock() + queueTableRows(projectWorkspace, [{ projectId: 'project-source' }]) + queueTableRows(projectWorkspace, [ + { project: { id: 'project-source', organizationId: null, archivedAt: null } }, + ]) /** * The fork transaction re-reads the parent's organization under the lock to * confirm it has not moved since `assertCanFork` captured the policy. * Matches POLICY.organizationId, so the fork proceeds. */ queueTableRows(workspace, [{ organizationId: null }]) + queueTableRows(workspace, [{ archivedAt: null, forkSyncNewWorkflowsExcluded: false }]) mockSumForkCopyBytes.mockResolvedValue(0) mockAssertForkStorageHeadroom.mockResolvedValue(undefined) mockLoadSourceDeployedStates.mockResolvedValue({ @@ -210,6 +215,10 @@ describe('createFork storage headroom gate', () => { it('refuses when the parent changed organizations after the policy was captured', async () => { resetDbChainMock() + queueTableRows(projectWorkspace, [{ projectId: 'project-source' }]) + queueTableRows(projectWorkspace, [ + { project: { id: 'project-source', organizationId: null, archivedAt: null } }, + ]) /** * `assertCanFork` captures `policy.organizationId` before this transaction, * so an admin workspace move committing in between would otherwise leave @@ -234,7 +243,12 @@ describe('createFork storage headroom gate', () => { */ it('gives the child the source workspace personal API-key and fork-sync policies', async () => { resetDbChainMock() + queueTableRows(projectWorkspace, [{ projectId: 'project-source' }]) + queueTableRows(projectWorkspace, [ + { project: { id: 'project-source', organizationId: null, archivedAt: null } }, + ]) queueTableRows(workspace, [{ organizationId: null, forkSyncNewWorkflowsExcluded: true }]) + queueTableRows(workspace, [{ archivedAt: null, forkSyncNewWorkflowsExcluded: true }]) const result = await createFork(forkParams()) diff --git a/apps/sim/ee/workspace-forking/lib/create-fork.ts b/apps/sim/ee/workspace-forking/lib/create-fork.ts index 8b8a06bbb77..1192b8985f2 100644 --- a/apps/sim/ee/workspace-forking/lib/create-fork.ts +++ b/apps/sim/ee/workspace-forking/lib/create-fork.ts @@ -1,5 +1,5 @@ import { db } from '@sim/db' -import { permissions, workflow, workspace } from '@sim/db/schema' +import { permissions, projectWorkspace, workflow, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' import type { PermissionType } from '@sim/platform-authz/workspace' import { getErrorMessage } from '@sim/utils/errors' @@ -7,6 +7,7 @@ import { generateId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' import type { Workspace } from '@/lib/api/contracts/workspaces' import { enqueueOutboxEvent } from '@/lib/core/outbox/service' +import { requireForkProject } from '@/lib/projects/membership' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' @@ -231,6 +232,7 @@ export async function createFork(params: CreateForkParams): Promise workspaceForkin import { unlinkForkEdge } from '@/ee/workspace-forking/lib/lineage/unlink' -const { mockSetForkLockTimeout, mockAcquireForkEdgeLock } = workspaceForkingLineageMockFns const { mockResolveForkLineageRootId } = workspaceForkingLineageRootMockFns const EDGE = { childWorkspaceId: 'child-ws', parentWorkspaceId: 'parent-ws' } @@ -27,21 +23,6 @@ describe('unlinkForkEdge', () => { mockResolveForkLineageRootId.mockResolvedValue('root-ws') }) - it('nulls the child pointer and purges all four edge tables under the edge lock', async () => { - dbChainMockFns.returning.mockResolvedValueOnce([{ id: 'child-ws' }]) - - const result = await unlinkForkEdge(EDGE, 'req-1') - - expect(result).toEqual({ unlinked: true }) - expect(mockSetForkLockTimeout).toHaveBeenCalledTimes(1) - expect(mockAcquireForkEdgeLock).toHaveBeenCalledWith(dbChainMock.db, 'child-ws') - expect(dbChainMockFns.update).toHaveBeenCalledTimes(1) - expect(dbChainMockFns.set).toHaveBeenCalledWith( - expect.objectContaining({ forkedFromWorkspaceId: null }) - ) - expect(dbChainMockFns.delete).toHaveBeenCalledTimes(4) - }) - /** A root moved by a concurrent unlink higher up means the lock taken no longer covers it. */ it('refuses when the lineage root moved before the lock was taken', async () => { mockResolveForkLineageRootId.mockResolvedValueOnce('root-ws').mockResolvedValueOnce('parent-ws') diff --git a/apps/sim/ee/workspace-forking/lib/lineage/unlink.ts b/apps/sim/ee/workspace-forking/lib/lineage/unlink.ts index 6540a3c4a96..e7e610dc20b 100644 --- a/apps/sim/ee/workspace-forking/lib/lineage/unlink.ts +++ b/apps/sim/ee/workspace-forking/lib/lineage/unlink.ts @@ -8,6 +8,7 @@ import { } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' +import { splitForkProject } from '@/lib/projects/membership' import { ForkError } from '@/ee/workspace-forking/lib/lineage/authz' import { acquireForkEdgeLock, @@ -27,8 +28,7 @@ export interface UnlinkForkResult { /** * Permanently dissolve a fork edge: null the child's `forkedFromWorkspaceId` (the * edge's single source of truth) and purge the edge's fork state — resource map, - * block map, dependent values, and promote-run undo points. Both workspaces are - * left untouched; only the association and its metadata are removed. + * block map, dependent values, and promote-run undo points. Both workspaces remain; the detached subtree moves into its own Project. * * Runs in one transaction under the lineage and edge advisory locks. Every promote and * rollback on the edge holds the edge lock, so an in-flight sync either finishes before @@ -62,6 +62,14 @@ export async function unlinkForkEdge( } await acquireForkEdgeLock(tx, childWorkspaceId) + const [currentEdge] = await tx + .select({ parentId: workspace.forkedFromWorkspaceId }) + .from(workspace) + .where(eq(workspace.id, childWorkspaceId)) + .limit(1) + if (currentEdge?.parentId !== parentWorkspaceId) return false + await splitForkProject(tx, childWorkspaceId) + const updated = await tx .update(workspace) .set({ forkedFromWorkspaceId: null, updatedAt: new Date() }) diff --git a/apps/sim/hooks/queries/projects.ts b/apps/sim/hooks/queries/projects.ts new file mode 100644 index 00000000000..c02e8c28353 --- /dev/null +++ b/apps/sim/hooks/queries/projects.ts @@ -0,0 +1,25 @@ +import { useInfiniteQuery } from '@tanstack/react-query' +import { requestJson } from '@/lib/api/client/request' +import { listProjectsContract } from '@/lib/api/contracts/projects' + +const PROJECT_LIST_STALE_TIME = 30_000 +const projectKeys = { + all: ['projects'] as const, + lists: () => [...projectKeys.all, 'list'] as const, + list: (organizationId: string) => [...projectKeys.lists(), organizationId] as const, +} + +export function useProjects(organizationId: string) { + return useInfiniteQuery({ + queryKey: projectKeys.list(organizationId), + initialPageParam: null as string | null, + queryFn: ({ signal, pageParam }) => + requestJson(listProjectsContract, { + query: { organizationId, cursor: pageParam ?? undefined, limit: 100 }, + signal, + }), + getNextPageParam: (page) => page.nextCursor, + staleTime: PROJECT_LIST_STALE_TIME, + enabled: Boolean(organizationId), + }) +} diff --git a/apps/sim/lib/api/contracts/projects.ts b/apps/sim/lib/api/contracts/projects.ts new file mode 100644 index 00000000000..85e774a9cd6 --- /dev/null +++ b/apps/sim/lib/api/contracts/projects.ts @@ -0,0 +1,86 @@ +import { z } from 'zod' +import { nonEmptyIdSchema } from '@/lib/api/contracts/primitives' +import { defineRouteContract } from '@/lib/api/contracts/types' + +export const projectEnvironmentSchema = z.object({ + id: nonEmptyIdSchema, + name: z.string(), + forkedFromWorkspaceId: nonEmptyIdSchema.nullable(), +}) +export type ProjectEnvironment = z.output +export const projectSchema = z.object({ + id: nonEmptyIdSchema, + name: z.string(), + organizationId: nonEmptyIdSchema.nullable(), + ownerId: nonEmptyIdSchema, + archivedAt: z.coerce.date().nullable(), + createdAt: z.coerce.date(), + updatedAt: z.coerce.date(), + environments: z.array(projectEnvironmentSchema), + capabilities: z.object({ administer: z.boolean(), issues: z.boolean() }), +}) +export type Project = z.output +export const projectParamsSchema = z.object({ id: nonEmptyIdSchema }) +export type ProjectParams = z.input +export const projectQuerySchema = z.object({ + organizationId: nonEmptyIdSchema.optional(), + workspaceId: nonEmptyIdSchema.optional(), +}) +export type ProjectQuery = z.input +export const listProjectsQuerySchema = z.object({ + organizationId: nonEmptyIdSchema.optional(), + cursor: nonEmptyIdSchema.optional(), + limit: z.coerce.number().int().min(1).max(100).default(50), +}) +export type ListProjectsQuery = z.input +export const listProjectsResponseSchema = z.object({ + projects: z.array(projectSchema), + nextCursor: nonEmptyIdSchema.nullable(), +}) +export type ListProjectsResponse = z.output +export const listProjectsContract = defineRouteContract({ + method: 'GET', + path: '/api/projects', + query: listProjectsQuerySchema, + response: { mode: 'json', schema: listProjectsResponseSchema }, +}) +export const getProjectResponseSchema = z.object({ project: projectSchema }) +export type GetProjectResponse = z.output +export const getProjectContract = defineRouteContract({ + method: 'GET', + path: '/api/projects/[id]', + params: projectParamsSchema, + query: projectQuerySchema, + response: { mode: 'json', schema: getProjectResponseSchema }, +}) +export const renameProjectBodySchema = z.object({ name: z.string().trim().min(1).max(100) }) +export type RenameProjectBody = z.input +export const renameProjectResponseSchema = z.object({ id: nonEmptyIdSchema, name: z.string() }) +export type RenameProjectResponse = z.output +export const renameProjectContract = defineRouteContract({ + method: 'PATCH', + path: '/api/projects/[id]', + params: projectParamsSchema, + body: renameProjectBodySchema, + response: { mode: 'json', schema: renameProjectResponseSchema }, +}) +export const archiveProjectResponseSchema = z.object({ + id: nonEmptyIdSchema, + archived: z.boolean(), +}) +export type ArchiveProjectResponse = z.output +export const archiveProjectContract = defineRouteContract({ + method: 'DELETE', + path: '/api/projects/[id]', + params: projectParamsSchema, + response: { mode: 'json', schema: archiveProjectResponseSchema }, +}) + +export const workspaceProjectParamsSchema = z.object({ workspaceId: nonEmptyIdSchema }) +export type WorkspaceProjectParams = z.input +export const getWorkspaceProjectContract = defineRouteContract({ + method: 'GET', + path: '/api/projects/by-workspace/[workspaceId]', + params: workspaceProjectParamsSchema, + response: { mode: 'json', schema: getProjectResponseSchema }, +}) diff --git a/apps/sim/lib/billing/organizations/lock-order.test.ts b/apps/sim/lib/billing/organizations/lock-order.test.ts index 11aa771177f..108bb594f63 100644 --- a/apps/sim/lib/billing/organizations/lock-order.test.ts +++ b/apps/sim/lib/billing/organizations/lock-order.test.ts @@ -228,6 +228,7 @@ describe('workspace payer-change transaction lock ordering', () => { const tx = { execute: async () => [], select, + selectDistinct: select, insert: () => ({ values: () => ({ onConflictDoUpdate: async () => undefined, diff --git a/apps/sim/lib/billing/organizations/membership.ts b/apps/sim/lib/billing/organizations/membership.ts index ce5181d8a27..ebbdbb0b0fe 100644 --- a/apps/sim/lib/billing/organizations/membership.ts +++ b/apps/sim/lib/billing/organizations/membership.ts @@ -15,6 +15,7 @@ import { organization, permissionGroupMember, permissions, + project, subscription as subscriptionTable, user, userStats, @@ -60,6 +61,7 @@ import { revokePersonalApiKeysTx, revokeUserSessionsTx, } from '@/lib/organizations/members/revocation' +import { tryLockProject } from '@/lib/projects/membership' import { removeWorkspaceSkillMembershipsTx } from '@/lib/skills/access' import { reassignWorkflowOwnershipForWorkspaceMemberRemovalTx, @@ -555,6 +557,25 @@ async function reassignOwnedOrganizationResourcesTx({ const ownerId = ownerMembership?.userId if (!ownerId || ownerId === userId) return 0 + const ownedProjects = await tx + .select({ id: project.id }) + .from(project) + .where(and(eq(project.organizationId, organizationId), eq(project.ownerId, userId))) + .orderBy(project.id) + for (const row of ownedProjects) { + await tryLockProject(tx, row.id) + await tx + .update(project) + .set({ ownerId, updatedAt: new Date() }) + .where( + and( + eq(project.id, row.id), + eq(project.ownerId, userId), + eq(project.organizationId, organizationId) + ) + ) + } + /** Creator attribution must survive account deletion without changing document ACLs. */ await tx .update(knowledgeBase) diff --git a/apps/sim/lib/permission-groups/capabilities.ts b/apps/sim/lib/permission-groups/capabilities.ts index 8260dea111d..ab3743d91a2 100644 --- a/apps/sim/lib/permission-groups/capabilities.ts +++ b/apps/sim/lib/permission-groups/capabilities.ts @@ -58,6 +58,7 @@ export const CAPABILITY_IDS = [ 'copilot.tool_auto_approval', 'sandboxes.use', 'knowledge.export', + 'project_issues.use', ] as const export type PermissionGroupCapability = (typeof CAPABILITY_IDS)[number] @@ -431,6 +432,14 @@ export const CAPABILITY_RULES = { describe: 'Exporting a knowledge base', deniedBy: (config) => config.disableKnowledgeBaseExport || config.hideKnowledgeBaseTab, }, + 'project_issues.use': { + kind: 'parameterized', + configKeys: ['deniedPartialAccessProjectIssues'], + detailCode: 'PERMISSION_GROUP_CAPABILITY_BLOCKED', + describe: 'Project Issues access', + deniedBy: (config: PermissionGroupConfig, projectId: string) => + config.deniedPartialAccessProjectIssues.includes(projectId), + }, } satisfies { readonly [K in PermissionGroupCapability]: CapabilityRule } /** diff --git a/apps/sim/lib/permission-groups/fields.test.ts b/apps/sim/lib/permission-groups/fields.test.ts index 45f99a25312..bd3070defc5 100644 --- a/apps/sim/lib/permission-groups/fields.test.ts +++ b/apps/sim/lib/permission-groups/fields.test.ts @@ -163,6 +163,7 @@ const fixtures: readonly CoercionFixture[] = [ hideSandboxesTab: true, disableOAuthAppAccess: true, disableKnowledgeBaseExport: true, + deniedPartialAccessProjectIssues: ['project-a'], }, expected: { allowedIntegrations: ['slack_v2'], @@ -207,6 +208,7 @@ const fixtures: readonly CoercionFixture[] = [ hideSandboxesTab: true, disableOAuthAppAccess: true, disableKnowledgeBaseExport: true, + deniedPartialAccessProjectIssues: ['project-a'], }, }, ] diff --git a/apps/sim/lib/permission-groups/fields.ts b/apps/sim/lib/permission-groups/fields.ts index 8bb73786daf..0ceb1036c70 100644 --- a/apps/sim/lib/permission-groups/fields.ts +++ b/apps/sim/lib/permission-groups/fields.ts @@ -506,6 +506,11 @@ export const PERMISSION_GROUP_FIELDS = { category: 'Knowledge Base', hint: 'Prevent downloading a whole knowledge base as an archive.', }), + deniedPartialAccessProjectIssues: denylist( + z.string().min(1).max(255), + 'capability', + 'Issues in the listed Projects are unavailable to teammates without access to every active environment.' + ), } satisfies Record export type PermissionGroupFields = typeof PERMISSION_GROUP_FIELDS diff --git a/apps/sim/lib/permission-groups/group-manager.ts b/apps/sim/lib/permission-groups/group-manager.ts index 3c492e42f09..db529c1ddfa 100644 --- a/apps/sim/lib/permission-groups/group-manager.ts +++ b/apps/sim/lib/permission-groups/group-manager.ts @@ -1,7 +1,12 @@ import { db } from '@sim/db' -import { permissionGroup, permissionGroupMember, permissionGroupWorkspace } from '@sim/db/schema' +import { + permissionGroup, + permissionGroupMember, + permissionGroupWorkspace, + project, +} from '@sim/db/schema' import { generateId } from '@sim/utils/id' -import { and, eq } from 'drizzle-orm' +import { and, eq, inArray } from 'drizzle-orm' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbOrTx } from '@/lib/db/types' import { @@ -54,6 +59,23 @@ async function validateWorkspaces( ) } +async function validateProjectRestrictions( + organizationId: string, + ids: string[] | undefined, + tx: DbOrTx +) { + if (!ids?.length) return + const projects = await tx + .select({ id: project.id }) + .from(project) + .where(and(eq(project.organizationId, organizationId), inArray(project.id, ids))) + if (projects.length !== new Set(ids).size) + throw new OrchestrationError( + 'validation', + 'A restricted Project does not belong to this organization' + ) +} + async function assertAvailableName( organizationId: string, name: string, @@ -115,6 +137,11 @@ export async function createPermissionGroupRecord( 'Select at least one workspace when the group targets specific workspaces' ) return withPermissionGroupMutation(organizationId, async (tx) => { + await validateProjectRestrictions( + organizationId, + input.config?.deniedPartialAccessProjectIssues, + tx + ) await validateWorkspaces(organizationId, workspaceIds, tx) await assertAvailableName(organizationId, input.name, tx) const now = new Date() @@ -151,6 +178,11 @@ export async function updatePermissionGroupRecord( updates: PermissionGroupChanges ) { return withPermissionGroupMutation(organizationId, async (tx) => { + await validateProjectRestrictions( + organizationId, + updates.config?.deniedPartialAccessProjectIssues, + tx + ) const group = await requirePermissionGroup(organizationId, groupId, tx) if (updates.name !== undefined) await assertAvailableName(organizationId, updates.name, tx, groupId) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts new file mode 100644 index 00000000000..0d8cdd291d5 --- /dev/null +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -0,0 +1,619 @@ +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { db } from '@sim/db' +import { + member, + organization, + permissionGroup, + permissions, + project, + projectWorkspace, + user, + userStats, + workflow, + workspace, + workspaceForkBlockMap, + workspaceForkDependentValue, + workspaceForkPromoteRun, + workspaceForkResourceMap, +} from '@sim/db/schema' +import { + createSessionPrincipal, + createWorkspaceApiKeyPrincipal, +} from '@sim/testing/factories/principal.factory' +import { getErrorMessage } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { and, eq, inArray } from 'drizzle-orm' +import { afterAll, describe, expect, it } from 'vitest' +import { removeUserFromOrganization } from '@/lib/billing/organizations/membership' +import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' +import { + archiveProject, + getProject, + getProjectIssueAccess, + getWorkspaceProject, + listProjects, + renameProject, +} from '@/lib/projects/application' +import { archiveProjectInTransaction } from '@/lib/projects/lifecycle' +import { createProjectForWorkspace, transferWorkspaceProjects } from '@/lib/projects/membership' +import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' +import { createWorkspaceInTransaction } from '@/lib/workspaces/create' +import { archiveWorkspace } from '@/lib/workspaces/lifecycle' +import { detachOrganizationWorkspacesTx } from '@/lib/workspaces/organization-workspaces' +import { getWorkspaceWithOwner } from '@/lib/workspaces/permissions/utils' +import { getWorkspaceCreationPolicy } from '@/lib/workspaces/policy' +import { createFork } from '@/ee/workspace-forking/lib/create-fork' +import { unlinkForkEdge } from '@/ee/workspace-forking/lib/lineage/unlink' + +const users: string[] = [] +const organizations: string[] = [] +const environments: string[] = [] +const request = { requestId: 'project-foundation-integration', headers: new Headers() } +const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = + [] + +/** Exercises durable auth and lifecycle invariants against real Postgres, including concurrent writers. */ +function check(name: string, run: () => Promise) { + it(name, async () => { + const started = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - started }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - started, + error: getErrorMessage(error), + }) + throw error + } + }) +} + +async function fixture(org = true, count = 2) { + const ownerId = generateId() + const teammateId = generateId() + const outsiderId = generateId() + const now = new Date() + for (const id of [ownerId, teammateId, outsiderId]) { + users.push(id) + await db.insert(user).values({ + id, + email: `${id}@projects.invalid`, + name: 'Project fixture', + emailVerified: true, + createdAt: now, + updatedAt: now, + }) + } + await db.insert(userStats).values({ id: generateId(), userId: ownerId }) + const organizationId = org ? generateId() : null + if (organizationId) { + organizations.push(organizationId) + await db + .insert(organization) + .values({ id: organizationId, name: 'Project fixture', slug: organizationId, createdAt: now }) + await db + .insert(member) + .values({ id: generateId(), organizationId, userId: ownerId, role: 'owner', createdAt: now }) + } + const ids = Array.from({ length: count }, () => generateId()) + let projectId = '' + await db.transaction(async (tx) => { + for (const [index, id] of ids.entries()) { + environments.push(id) + await tx.insert(workspace).values({ + id, + name: `Environment ${index}`, + ownerId, + billedAccountUserId: ownerId, + organizationId, + workspaceMode: organizationId ? 'organization' : 'grandfathered_shared', + forkedFromWorkspaceId: index ? ids[index - 1] : null, + }) + await tx.insert(permissions).values({ + id: generateId(), + entityType: 'workspace', + entityId: id, + userId: ownerId, + permissionType: 'admin', + }) + if (!index) + projectId = await createProjectForWorkspace(tx, { + workspaceId: id, + name: 'Environment 0', + organizationId, + ownerId, + }) + else await tx.insert(projectWorkspace).values({ projectId, workspaceId: id }) + } + }) + const owner = createSessionPrincipal({ userId: ownerId }) + const teammate = createSessionPrincipal({ userId: teammateId }) + await db.insert(permissions).values({ + id: generateId(), + entityType: 'workspace', + entityId: ids[0], + userId: teammateId, + permissionType: 'admin', + }) + return { ownerId, teammateId, outsiderId, organizationId, projectId, ids, owner, teammate } +} + +async function addWorkflow(workspaceId: string, userId: string) { + const id = generateId() + const now = new Date() + await db.insert(workflow).values({ + id, + workspaceId, + userId, + name: 'Scheduled work', + createdAt: now, + updatedAt: now, + lastSynced: now, + isDeployed: true, + isPublicApi: true, + }) + return id +} + +afterAll(async () => { + const reportPath = + process.env.PROJECT_FOUNDATION_REPORT_PATH ?? resolve('test-results/project-foundation.json') + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) + if (environments.length) { + const memberships = await db + .select({ id: projectWorkspace.projectId }) + .from(projectWorkspace) + .where(inArray(projectWorkspace.workspaceId, environments)) + const ids = [...new Set(memberships.map((row) => row.id))] + await db.delete(projectWorkspace).where(inArray(projectWorkspace.workspaceId, environments)) + if (ids.length) await db.delete(project).where(inArray(project.id, ids)) + await db.delete(workspace).where(inArray(workspace.id, environments)) + } + if (organizations.length) + await db.delete(organization).where(inArray(organization.id, organizations)) + if (users.length) await db.delete(user).where(inArray(user.id, users)) +}) + +describe('Project foundation at the database and application boundary', () => { + check('workspace creation and forks commit exactly one Project membership', async () => { + const f = await fixture(false, 1) + const source = await db.transaction((tx) => + createWorkspaceInTransaction(tx, { + userId: f.ownerId, + name: 'New environment', + organizationId: null, + observedOrganizationId: null, + governingPermissionGroupOrganizationId: null, + workspaceMode: 'personal', + billedAccountUserId: f.ownerId, + skipDefaultWorkflow: true, + }) + ) + environments.push(source.id) + const before = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, source.id)) + expect(before).toHaveLength(1) + const policy = await getWorkspaceCreationPolicy({ userId: f.ownerId }) + const parent = await getWorkspaceWithOwner(source.id) + if (!parent) throw new Error('Missing source fixture') + const fork = await createFork({ + source: parent, + policy, + userId: f.ownerId, + name: 'Child environment', + }) + environments.push(fork.workspace.id) + const child = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) + expect(child).toHaveLength(1) + expect(child[0].projectId).toBe(before[0].projectId) + }) + + check( + 'a departing organization member transfers Project lifecycle ownership to the org owner', + async () => { + const f = await fixture() + const organizationId = f.organizationId + if (!organizationId) throw new Error('Missing organization fixture') + const memberId = generateId() + await db.insert(member).values({ + id: memberId, + organizationId: organizationId, + userId: f.teammateId, + role: 'member', + createdAt: new Date(), + }) + await db.update(project).set({ ownerId: f.teammateId }).where(eq(project.id, f.projectId)) + const result = await removeUserFromOrganization({ + userId: f.teammateId, + organizationId: organizationId, + memberId, + actorUserId: f.ownerId, + skipBillingLogic: true, + onError: 'throw', + }) + expect(result.success).toBe(true) + const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(record.ownerId).toBe(f.ownerId) + expect(record.organizationId).toBe(f.organizationId) + } + ) + + check( + 'hides inaccessible environments and denies mismatched scopes and workspace keys', + async () => { + const f = await fixture() + const input = { projectId: f.projectId } + const result = await getProject.execute({ principal: f.teammate, input, request }) + expect(result.project.environments.map((row) => row.id)).toEqual([f.ids[0]]) + expect(result.project.capabilities).toEqual({ administer: false, issues: true }) + await expect( + getWorkspaceProject.execute({ + principal: f.teammate, + input: { workspaceId: f.ids[1] }, + request, + }) + ).rejects.toMatchObject({ code: 'not_found' }) + await expect( + getProject.execute({ + principal: f.teammate, + input: { ...input, organizationId: generateId() }, + request, + }) + ).rejects.toMatchObject({ code: 'not_found' }) + await expect( + getProject.execute({ + principal: createSessionPrincipal({ userId: f.outsiderId }), + input, + request, + }) + ).rejects.toMatchObject({ code: 'not_found' }) + await expect( + getProject.execute({ + principal: createWorkspaceApiKeyPrincipal({ workspaceId: f.ids[0] }), + input, + request, + }) + ).rejects.toThrow() + await expect( + renameProject.execute({ + principal: f.teammate, + input: { ...input, name: 'Renamed' }, + request, + }) + ).rejects.toMatchObject({ code: 'forbidden' }) + const list = await listProjects.execute({ + principal: f.teammate, + input: { limit: 1 }, + request, + }) + expect(list.projects.map((row) => row.id)).toEqual([f.projectId]) + } + ) + + check( + 'Issues restriction applies only to partial access and excludes archived environments', + async () => { + const f = await fixture() + const organizationId = f.organizationId + if (!organizationId) throw new Error('Missing organization fixture') + const args = { principal: f.teammate, input: { projectId: f.projectId }, request } + await expect(getProjectIssueAccess.execute(args)).resolves.toEqual({ projectId: f.projectId }) + await db.insert(permissionGroup).values({ + id: generateId(), + organizationId: organizationId, + name: 'Default restrictions', + createdBy: f.ownerId, + isDefault: true, + config: { deniedPartialAccessProjectIssues: [f.projectId] }, + }) + await expect(getProjectIssueAccess.execute(args)).rejects.toMatchObject({ + detailCode: 'PERMISSION_GROUP_CAPABILITY_BLOCKED', + }) + await db.insert(permissions).values({ + id: generateId(), + userId: f.teammateId, + entityType: 'workspace', + entityId: f.ids[1], + permissionType: 'read', + }) + await expect(getProjectIssueAccess.execute(args)).resolves.toEqual({ projectId: f.projectId }) + await db + .delete(permissions) + .where(and(eq(permissions.userId, f.teammateId), eq(permissions.entityId, f.ids[1]))) + await archiveWorkspace(f.ids[1], request) + await expect(getProjectIssueAccess.execute(args)).resolves.toEqual({ projectId: f.projectId }) + await expect( + renameProject.execute({ + ...args, + input: { ...args.input, name: 'Still requires all admins' }, + }) + ).rejects.toMatchObject({ code: 'forbidden' }) + } + ) + + check( + 'personal ownership does not substitute for access; admins of every environment may rename', + async () => { + const f = await fixture(false) + await db.insert(permissions).values({ + id: generateId(), + userId: f.teammateId, + entityType: 'workspace', + entityId: f.ids[1], + permissionType: 'admin', + }) + await expect( + renameProject.execute({ + principal: f.teammate, + input: { projectId: f.projectId, name: 'Personal project' }, + request, + }) + ).resolves.toEqual({ id: f.projectId, name: 'Personal project' }) + await db.delete(permissions).where(eq(permissions.userId, f.ownerId)) + await expect( + getProject.execute({ principal: f.owner, input: { projectId: f.projectId }, request }) + ).rejects.toMatchObject({ code: 'not_found' }) + } + ) + + check('concurrent individual removals preserve the last active environment', async () => { + const f = await fixture(false) + const results = await Promise.allSettled(f.ids.map((id) => archiveWorkspace(id, request))) + expect(results.filter((result) => result.status === 'fulfilled')).toHaveLength(1) + expect(results.filter((result) => result.status === 'rejected')).toHaveLength(1) + const rows = await db.select().from(workspace).where(inArray(workspace.id, f.ids)) + expect(rows.filter((row) => !row.archivedAt)).toHaveLength(1) + }) + + check( + 'retrying an environment archive repairs previously unarchived child workflows', + async () => { + const f = await fixture(false) + await archiveWorkspace(f.ids[1], request) + const workflowId = await addWorkflow(f.ids[1], f.ownerId) + expect(await archiveWorkspace(f.ids[1], request)).toMatchObject({ archived: false }) + const [row] = await db.select().from(workflow).where(eq(workflow.id, workflowId)) + expect(row.archivedAt).not.toBeNull() + expect(row.isDeployed).toBe(false) + expect(row.isPublicApi).toBe(false) + } + ) + + check( + 'Project archive rolls back as a unit and retries without losing environments', + async () => { + const f = await fixture() + const workflowIds = await Promise.all(f.ids.map((id) => addWorkflow(id, f.ownerId))) + await expect( + db.transaction(async (tx) => { + await archiveProjectInTransaction(tx, f.projectId) + throw new Error('Abort compound archive') + }) + ).rejects.toThrow('Abort compound archive') + const before = await db.select().from(workspace).where(inArray(workspace.id, f.ids)) + expect(before.every((row) => row.archivedAt === null)).toBe(true) + const args = { principal: f.owner, input: { projectId: f.projectId }, request } + await archiveProject.execute(args) + await archiveProject.execute(args) + await expect( + db.transaction(async (tx) => { + const row = await buildNewWorkflowRow(tx, { + id: generateId(), + userId: f.ownerId, + workspaceId: f.ids[0], + folderId: null, + name: 'Late workflow', + description: null, + }) + await tx.insert(workflow).values(row) + }) + ).rejects.toMatchObject({ code: 'not_found' }) + + const rows = await db.select().from(workspace).where(inArray(workspace.id, f.ids)) + expect(rows.every((row) => row.archivedAt !== null)).toBe(true) + const workflows = await db.select().from(workflow).where(inArray(workflow.id, workflowIds)) + expect( + workflows.every((row) => row.archivedAt !== null && !row.isDeployed && !row.isPublicApi) + ).toBe(true) + expect( + await db.select().from(projectWorkspace).where(eq(projectWorkspace.projectId, f.projectId)) + ).toHaveLength(2) + await expect(getProjectIssueAccess.execute(args)).rejects.toMatchObject({ code: 'conflict' }) + } + ) + + check( + 'disconnect moves descendants to one new Project and preserves restrictions on retry', + async () => { + const f = await fixture(true, 3) + const organizationId = f.organizationId + if (!organizationId) throw new Error('Missing organization fixture') + const groupId = generateId() + await db.insert(permissionGroup).values({ + id: groupId, + organizationId: organizationId, + name: 'Restricted', + createdBy: f.ownerId, + isDefault: true, + config: { deniedPartialAccessProjectIssues: [f.projectId] }, + }) + for (const childWorkspaceId of [f.ids[1], f.ids[2]]) { + await db.insert(workspaceForkResourceMap).values({ + id: generateId(), + childWorkspaceId, + resourceType: 'workflow', + parentResourceId: 'parent-workflow', + childResourceId: 'child-workflow', + }) + await db.insert(workspaceForkBlockMap).values({ + id: generateId(), + childWorkspaceId, + parentWorkflowId: 'parent-workflow', + childWorkflowId: 'child-workflow', + parentBlockId: 'parent-block', + childBlockId: 'child-block', + }) + await db.insert(workspaceForkDependentValue).values({ + id: generateId(), + childWorkspaceId, + targetWorkflowId: 'child-workflow', + targetBlockId: 'child-block', + subBlockKey: 'selection', + value: 'saved-selection', + }) + await db.insert(workspaceForkPromoteRun).values({ + id: generateId(), + childWorkspaceId, + sourceWorkspaceId: f.ids[0], + targetWorkspaceId: childWorkspaceId, + direction: 'pull', + snapshot: {}, + }) + } + const edge = { parentWorkspaceId: f.ids[0], childWorkspaceId: f.ids[1] } + expect(await unlinkForkEdge(edge)).toEqual({ unlinked: true }) + for (const table of [ + workspaceForkResourceMap, + workspaceForkBlockMap, + workspaceForkDependentValue, + workspaceForkPromoteRun, + ]) { + const rows = await db + .select({ childWorkspaceId: table.childWorkspaceId }) + .from(table) + .where(inArray(table.childWorkspaceId, [f.ids[1], f.ids[2]])) + expect(rows).toEqual([{ childWorkspaceId: f.ids[2] }]) + } + const first = await db + .select() + .from(projectWorkspace) + .where(inArray(projectWorkspace.workspaceId, f.ids)) + const root = first.find((row) => row.workspaceId === f.ids[0]) + const child = first.find((row) => row.workspaceId === f.ids[1]) + const grandchild = first.find((row) => row.workspaceId === f.ids[2]) + if (!root || !child || !grandchild) throw new Error('Missing family membership fixture') + expect(root.projectId).toBe(f.projectId) + expect(child.projectId).not.toBe(f.projectId) + expect(grandchild.projectId).toBe(child.projectId) + expect(await unlinkForkEdge(edge)).toEqual({ unlinked: false }) + const [group] = await db.select().from(permissionGroup).where(eq(permissionGroup.id, groupId)) + expect(group.config).toEqual({ + deniedPartialAccessProjectIssues: [f.projectId, child.projectId], + }) + const second = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, f.ids[1])) + expect(second[0].projectId).toBe(child.projectId) + } + ) + + check( + 'disconnect refuses a partially assigned subtree without moving any environments', + async () => { + const f = await fixture(true, 3) + await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[2])) + await expect( + unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: f.ids[1] }) + ).rejects.toMatchObject({ code: 'conflict' }) + const [child] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, f.ids[1])) + expect(child.projectId).toBe(f.projectId) + const [edge] = await db.select().from(workspace).where(eq(workspace.id, f.ids[1])) + expect(edge.forkedFromWorkspaceId).toBe(f.ids[0]) + } + ) + + check( + 'organization moves remove obsolete Project restrictions while preserving unrelated policy', + async () => { + const source = await fixture(true, 1) + const organizationId = source.organizationId + if (!organizationId) throw new Error('Missing organization fixture') + const destination = await fixture(true, 1) + const groupId = generateId() + await db.insert(permissionGroup).values({ + id: groupId, + organizationId: organizationId, + name: 'Source policy', + createdBy: source.ownerId, + isDefault: true, + config: { deniedPartialAccessProjectIssues: [source.projectId], hideTablesTab: true }, + }) + await db.transaction(async (tx) => { + await transferWorkspaceProjects(tx, source.ids, destination.organizationId) + await tx + .update(workspace) + .set({ organizationId: destination.organizationId }) + .where(eq(workspace.id, source.ids[0])) + }) + const [group] = await db.select().from(permissionGroup).where(eq(permissionGroup.id, groupId)) + expect(group.config).toEqual({ deniedPartialAccessProjectIssues: [], hideTablesTab: true }) + const [moved] = await db.select().from(project).where(eq(project.id, source.projectId)) + expect(moved).toMatchObject({ + organizationId: destination.organizationId, + ownerId: source.ownerId, + }) + } + ) + + check( + 'organization deletion preserves Project identity and assigns its former owner explicitly', + async () => { + const f = await fixture() + const organizationId = f.organizationId + if (!organizationId) throw new Error('Missing organization fixture') + await db.transaction(async (tx) => { + await detachOrganizationWorkspacesTx(tx, organizationId) + await tx.delete(organization).where(eq(organization.id, organizationId)) + }) + const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(record).toMatchObject({ id: f.projectId, organizationId: null, ownerId: f.ownerId }) + const result = await getProject.execute({ + principal: f.owner, + input: { projectId: f.projectId }, + request, + }) + expect(result.project.environments).toHaveLength(2) + await expect( + db.transaction((tx) => transferWorkspaceProjects(tx, [f.ids[0]], generateId())) + ).rejects.toMatchObject({ code: 'conflict' }) + } + ) + + check( + 'account teardown transfers surviving Projects and atomically removes wholly private Projects', + async () => { + const f = await fixture(false) + await db.insert(permissions).values({ + id: generateId(), + userId: f.teammateId, + entityType: 'workspace', + entityId: f.ids[1], + permissionType: 'admin', + }) + await db.transaction((tx) => prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[0]])) + const [survivor] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(survivor.ownerId).toBe(f.teammateId) + const privateProject = await fixture(false, 1) + await db.transaction((tx) => + prepareProjectsForAccountDeletion(tx, privateProject.ownerId, privateProject.ids) + ) + expect( + await db.select().from(project).where(eq(project.id, privateProject.projectId)) + ).toEqual([]) + } + ) +}) diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts new file mode 100644 index 00000000000..541ea922767 --- /dev/null +++ b/apps/sim/lib/projects/account-deletion.ts @@ -0,0 +1,83 @@ +import { permissions, project, projectWorkspace, workspace } from '@sim/db/schema' +import { and, asc, eq, inArray, ne, or, sql } from 'drizzle-orm' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { DbTransaction } from '@/lib/db/types' +import { lockProject } from '@/lib/projects/membership' + +/** Account teardown may erase a wholly private Project, but never strand a surviving one. */ +export async function prepareProjectsForAccountDeletion( + tx: DbTransaction, + userId: string, + doomedWorkspaceIds: string[] +): Promise { + const records = await tx + .select({ id: project.id }) + .from(project) + .where( + or( + eq(project.ownerId, userId), + doomedWorkspaceIds.length + ? sql`${project.id} in ( + select ${projectWorkspace.projectId} from ${projectWorkspace} + where ${inArray(projectWorkspace.workspaceId, doomedWorkspaceIds)} + )` + : undefined + ) + ) + .orderBy(asc(project.id)) + const doomed = new Set(doomedWorkspaceIds) + for (const { id } of records) { + await lockProject(tx, id) + const [record] = await tx.select().from(project).where(eq(project.id, id)) + if (!record) continue + const members = await tx + .select({ id: workspace.id, archivedAt: workspace.archivedAt }) + .from(projectWorkspace) + .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) + .where(eq(projectWorkspace.projectId, id)) + const survivors = members.filter((row) => !doomed.has(row.id)) + if (!survivors.length) { + if (record.organizationId || record.ownerId !== userId) + throw new OrchestrationError( + 'conflict', + 'Account deletion cannot remove another owner’s Project' + ) + await tx.delete(projectWorkspace).where(eq(projectWorkspace.projectId, id)) + await tx.delete(project).where(eq(project.id, id)) + continue + } + if (!record.archivedAt && survivors.every((row) => row.archivedAt)) + throw new OrchestrationError( + 'conflict', + 'Archive the Project before deleting its last active environment with your account' + ) + if (record.ownerId !== userId) continue + const admins = await tx + .select({ userId: permissions.userId }) + .from(permissions) + .where( + and( + eq(permissions.entityType, 'workspace'), + eq(permissions.permissionType, 'admin'), + ne(permissions.userId, userId), + inArray( + permissions.entityId, + survivors.map((row) => row.id) + ) + ) + ) + .groupBy(permissions.userId) + .having(sql`count(*) = ${survivors.length}`) + .orderBy(asc(permissions.userId)) + .limit(1) + if (!admins[0]) + throw new OrchestrationError( + 'conflict', + 'Give a teammate admin access to every environment before deleting the Project owner’s account' + ) + await tx + .update(project) + .set({ ownerId: admins[0].userId, updatedAt: new Date() }) + .where(eq(project.id, id)) + } +} diff --git a/apps/sim/lib/projects/application/authorization.ts b/apps/sim/lib/projects/application/authorization.ts new file mode 100644 index 00000000000..a0cf6b571a4 --- /dev/null +++ b/apps/sim/lib/projects/application/authorization.ts @@ -0,0 +1,128 @@ +import type { Principal, SessionPrincipal } from '@sim/auth/principal' +import { member, permissions, project, projectWorkspace, workspace } from '@sim/db/schema' +import { isOrgAdminRole } from '@sim/platform-authz/workspace' +import { and, asc, eq, inArray } from 'drizzle-orm' +import { PrincipalKindAuthorizationError } from '@/lib/core/application/workspace-authorization' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { DbTransaction } from '@/lib/db/types' +import { CAPABILITY_RULES, refuseCapability } from '@/lib/permission-groups/capabilities' +import { acquirePermissionGroupOrgLock } from '@/lib/permission-groups/locks' +import { resolveVerifiedUserAccessControlContext } from '@/lib/permission-groups/resolve.server' +import type { ProjectOperation } from '@/lib/projects/application/operations' +import { lockProject } from '@/lib/projects/membership' + +export function requireProjectPrincipal( + principal: Principal, + operation: ProjectOperation +): asserts principal is SessionPrincipal { + if (principal.kind !== 'session') + throw new PrincipalKindAuthorizationError(principal.kind, operation.id) +} + +/** The complete environment set is loaded server-side; hidden environments never enter the result. */ +export async function authorizeProject( + tx: DbTransaction, + principal: SessionPrincipal, + operation: ProjectOperation, + input: { projectId: string; organizationId?: string; workspaceId?: string } +) { + await lockProject(tx, input.projectId) + const [record] = await tx.select().from(project).where(eq(project.id, input.projectId)).limit(1) + if ( + !record || + (input.organizationId !== undefined && input.organizationId !== record.organizationId) + ) { + throw new OrchestrationError('not_found', 'Project not found') + } + const [orgMember] = record.organizationId + ? await tx + .select({ role: member.role }) + .from(member) + .where( + and(eq(member.userId, principal.userId), eq(member.organizationId, record.organizationId)) + ) + .limit(1) + .for('share') + : [] + const orgAdmin = isOrgAdminRole(orgMember?.role) + const environments = await tx + .select({ + id: workspace.id, + name: workspace.name, + organizationId: workspace.organizationId, + archivedAt: workspace.archivedAt, + parentId: workspace.forkedFromWorkspaceId, + }) + .from(projectWorkspace) + .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) + .where(eq(projectWorkspace.projectId, record.id)) + .orderBy(asc(workspace.id)) + if (environments.some((row) => row.organizationId !== record.organizationId)) + throw new OrchestrationError('conflict', 'Project ownership needs reconciliation') + const grants = environments.length + ? await tx + .select({ id: permissions.entityId, permission: permissions.permissionType }) + .from(permissions) + .where( + and( + eq(permissions.entityType, 'workspace'), + eq(permissions.userId, principal.userId), + inArray( + permissions.entityId, + environments.map((row) => row.id) + ) + ) + ) + .orderBy(asc(permissions.entityId)) + .for('share') + : [] + const grantsById = new Map(grants.map((row) => [row.id, row.permission])) + const rows = environments.map((row) => ({ ...row, permission: grantsById.get(row.id) ?? null })) + if (operation.access === 'issues' && record.organizationId) + await acquirePermissionGroupOrgLock(tx, record.organizationId) + const active = rows.filter((row) => !row.archivedAt) + const visible = active.filter((row) => orgAdmin || row.permission !== null) + const canAdminister = + orgAdmin || (rows.length > 0 && rows.every((row) => row.permission === 'admin')) + if (!visible.length && !(record.archivedAt && canAdminister)) + throw new OrchestrationError('not_found', 'Project not found') + if (input.workspaceId && !visible.some((row) => row.id === input.workspaceId)) + throw new OrchestrationError('not_found', 'Project not found') + if (operation.access === 'admin' && !canAdminister) + throw new OrchestrationError( + 'forbidden', + 'Organization admin or admin access to every environment is required' + ) + let canUseIssues = !record.archivedAt && visible.length > 0 + if (canUseIssues && visible.length < active.length && record.organizationId) { + for (const environment of visible) { + const { config } = await resolveVerifiedUserAccessControlContext( + principal.userId, + environment.id, + record.organizationId, + tx + ) + if (config && CAPABILITY_RULES['project_issues.use'].deniedBy(config, record.id)) { + canUseIssues = false + break + } + } + } + // permission-group-enforced: project_issues.use — Project-wide all-or-nothing gate follows current environment access. + if (operation.access === 'issues' && !canUseIssues) { + if (record.archivedAt) throw new OrchestrationError('conflict', 'Project is archived') + refuseCapability('project_issues.use') + } + const visibleIds = new Set(visible.map((row) => row.id)) + return { + record, + environmentIds: rows.map((row) => row.id), + canAdminister, + canUseIssues, + environments: visible.map((row) => ({ + id: row.id, + name: row.name, + forkedFromWorkspaceId: row.parentId && visibleIds.has(row.parentId) ? row.parentId : null, + })), + } +} diff --git a/apps/sim/lib/projects/application/index.ts b/apps/sim/lib/projects/application/index.ts new file mode 100644 index 00000000000..22b91983164 --- /dev/null +++ b/apps/sim/lib/projects/application/index.ts @@ -0,0 +1,9 @@ +export { projectOperations } from '@/lib/projects/application/operations' +export { + archiveProject, + getProject, + getProjectIssueAccess, + getWorkspaceProject, + listProjects, + renameProject, +} from '@/lib/projects/application/use-cases' diff --git a/apps/sim/lib/projects/application/operations.ts b/apps/sim/lib/projects/application/operations.ts new file mode 100644 index 00000000000..d95eaa5c6e0 --- /dev/null +++ b/apps/sim/lib/projects/application/operations.ts @@ -0,0 +1,52 @@ +import type { ApplicationOperation } from '@/lib/core/application/operation' +import { assertOperationCapability } from '@/lib/core/application/operation' + +export interface ProjectOperation extends ApplicationOperation { + readonly principalKinds: readonly ['session'] + readonly access: 'read' | 'admin' | 'issues' +} + +/** Project operations cannot borrow authority from workspace keys or an arbitrary root. */ +function defineProjectOperation(operation: O): O { + assertOperationCapability(operation) + Object.freeze(operation.principalKinds) + return Object.freeze(operation) +} + +export const projectOperations = { + // permission-group-exempt: navigation exposes only Projects containing accessible environments. + list: defineProjectOperation({ + id: 'projects.list', + principalKinds: ['session'], + access: 'read', + capability: 'none', + }), + // permission-group-exempt: Project metadata does not grant access to its Issues or environments. + get: defineProjectOperation({ + id: 'projects.get', + principalKinds: ['session'], + access: 'read', + capability: 'none', + }), + // permission-group-exempt: Project names are administered by org admins or admins of every environment. + rename: defineProjectOperation({ + id: 'projects.rename', + principalKinds: ['session'], + access: 'admin', + capability: 'none', + }), + // permission-group-exempt: archival revokes execution rather than granting a governed capability. + archive: defineProjectOperation({ + id: 'projects.archive', + principalKinds: ['session'], + access: 'admin', + capability: 'none', + }), + // permission-group-exempt: project_issues.use is parameterized by Project ID and checked after access. + issues: defineProjectOperation({ + id: 'projects.issues.access', + principalKinds: ['session'], + access: 'issues', + capability: 'none', + }), +} as const diff --git a/apps/sim/lib/projects/application/use-cases.ts b/apps/sim/lib/projects/application/use-cases.ts new file mode 100644 index 00000000000..d67a2c52b89 --- /dev/null +++ b/apps/sim/lib/projects/application/use-cases.ts @@ -0,0 +1,204 @@ +import { AuditAction, AuditResourceType } from '@sim/audit' +import { db } from '@sim/db' +import { member, permissions, project, projectWorkspace, workspace } from '@sim/db/schema' +import { and, asc, eq, gt, isNull, sql } from 'drizzle-orm' +import { recordProjectedUseCaseAuditEntries } from '@/lib/core/application/authorized-workspace-use-case' +import type { OperationUseCase } from '@/lib/core/application/operation' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { authorizeProject, requireProjectPrincipal } from '@/lib/projects/application/authorization' +import { projectOperations } from '@/lib/projects/application/operations' +import { archiveProjectInTransaction, finishProjectArchive } from '@/lib/projects/lifecycle' + +interface ProjectInput { + projectId: string + organizationId?: string + workspaceId?: string +} +type ProjectContext = Awaited> +function presentProject(context: ProjectContext) { + return { + ...context.record, + environments: context.environments, + capabilities: { administer: context.canAdminister, issues: context.canUseIssues }, + } +} + +export const getProject: OperationUseCase< + typeof projectOperations.get, + ProjectInput, + { project: ReturnType } +> = { + operation: projectOperations.get, + async execute({ principal, input }) { + requireProjectPrincipal(principal, projectOperations.get) + return db.transaction(async (tx) => ({ + project: presentProject(await authorizeProject(tx, principal, projectOperations.get, input)), + })) + }, +} + +/** Read-only capability probe. Issue mutations call authorizeProject inside their own transaction. */ +export const getProjectIssueAccess: OperationUseCase< + typeof projectOperations.issues, + ProjectInput, + { projectId: string } +> = { + operation: projectOperations.issues, + async execute({ principal, input }) { + requireProjectPrincipal(principal, projectOperations.issues) + return db.transaction(async (tx) => { + const context = await authorizeProject(tx, principal, projectOperations.issues, input) + return { projectId: context.record.id } + }) + }, +} + +export const listProjects: OperationUseCase< + typeof projectOperations.list, + { organizationId?: string; cursor?: string; limit: number }, + { projects: ReturnType[]; nextCursor: string | null } +> = { + operation: projectOperations.list, + async execute({ principal, input }) { + requireProjectPrincipal(principal, projectOperations.list) + if (!Number.isInteger(input.limit) || input.limit < 1 || input.limit > 100) + throw new OrchestrationError('validation', 'Limit must be between 1 and 100') + return db.transaction(async (tx) => { + const candidates = await tx + .select({ id: project.id }) + .from(project) + .where( + and( + isNull(project.archivedAt), + input.organizationId ? eq(project.organizationId, input.organizationId) : undefined, + input.cursor ? gt(project.id, input.cursor) : undefined, + sql`exists (select 1 from ${projectWorkspace} pw join ${workspace} w on w.id = pw.workspace_id + where pw.project_id = ${project.id} and w.archived_at is null and ( + exists (select 1 from ${permissions} pe where pe.entity_type = 'workspace' and pe.entity_id = w.id and pe.user_id = ${principal.userId}) + or exists (select 1 from ${member} m where m.organization_id = w.organization_id and m.user_id = ${principal.userId} and m.role in ('owner', 'admin')) + ))` + ) + ) + .orderBy(asc(project.id)) + .limit(input.limit + 1) + const page = candidates.slice(0, input.limit) + const projects = [] + for (const row of page) + projects.push( + presentProject( + await authorizeProject(tx, principal, projectOperations.list, { + projectId: row.id, + organizationId: input.organizationId, + }) + ) + ) + return { + projects, + nextCursor: candidates.length > input.limit ? (page.at(-1)?.id ?? null) : null, + } + }) + }, +} + +export const renameProject: OperationUseCase< + typeof projectOperations.rename, + ProjectInput & { name: string }, + { id: string; name: string } +> = { + operation: projectOperations.rename, + async execute({ principal, input, request }) { + requireProjectPrincipal(principal, projectOperations.rename) + const name = input.name.trim() + if (!name || name.length > 100) + throw new OrchestrationError('validation', 'Project name must contain 1–100 characters') + const result = await db.transaction(async (tx) => { + const context = await authorizeProject(tx, principal, projectOperations.rename, input) + if (context.record.archivedAt) throw new OrchestrationError('conflict', 'Project is archived') + if (context.record.name === name) return { context, changed: false } + await tx + .update(project) + .set({ name, updatedAt: new Date() }) + .where(eq(project.id, context.record.id)) + return { context, changed: true } + }) + if (result.changed) + recordProjectedUseCaseAuditEntries( + projectOperations.rename, + null, + principal, + request, + [ + { + action: AuditAction.PROJECT_UPDATED, + resourceType: AuditResourceType.PROJECT, + resourceId: input.projectId, + resourceName: name, + }, + ], + result.context.record.organizationId ?? undefined + ) + return { id: input.projectId, name } + }, +} + +export const archiveProject: OperationUseCase< + typeof projectOperations.archive, + ProjectInput, + { id: string; archived: boolean } +> = { + operation: projectOperations.archive, + async execute({ principal, input, request }) { + requireProjectPrincipal(principal, projectOperations.archive) + const result = await db.transaction(async (tx) => { + const context = await authorizeProject(tx, principal, projectOperations.archive, input) + const effects = await archiveProjectInTransaction(tx, context.record.id) + return { context, effects } + }) + if (!result.context.record.archivedAt) + recordProjectedUseCaseAuditEntries( + projectOperations.archive, + null, + principal, + request, + [ + { + action: AuditAction.PROJECT_ARCHIVED, + resourceType: AuditResourceType.PROJECT, + resourceId: input.projectId, + resourceName: result.context.record.name, + }, + ], + result.context.record.organizationId ?? undefined + ) + await finishProjectArchive(result.effects, `project:${input.projectId}`) + return { id: input.projectId, archived: true } + }, +} + +/** Resolves membership and authorizes the requested environment before returning Project data. */ +export const getWorkspaceProject: OperationUseCase< + typeof projectOperations.get, + { workspaceId: string }, + { project: ReturnType } +> = { + operation: projectOperations.get, + async execute({ principal, input }) { + requireProjectPrincipal(principal, projectOperations.get) + return db.transaction(async (tx) => { + const [membership] = await tx + .select({ projectId: projectWorkspace.projectId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, input.workspaceId)) + .limit(1) + if (!membership) throw new OrchestrationError('not_found', 'Project not found') + return { + project: presentProject( + await authorizeProject(tx, principal, projectOperations.get, { + projectId: membership.projectId, + workspaceId: input.workspaceId, + }) + ), + } + }) + }, +} diff --git a/apps/sim/lib/projects/lifecycle.ts b/apps/sim/lib/projects/lifecycle.ts new file mode 100644 index 00000000000..d089f7c2e38 --- /dev/null +++ b/apps/sim/lib/projects/lifecycle.ts @@ -0,0 +1,66 @@ +import { + project, + projectWorkspace, + workflow, + workflowMcpServer, + workflowMcpTool, + workspace, +} from '@sim/db/schema' +import { and, asc, eq, isNull } from 'drizzle-orm' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { DbTransaction } from '@/lib/db/types' +import { lockProject } from '@/lib/projects/membership' +import { archiveWorkflowInTransaction, finishWorkflowArchive } from '@/lib/workflows/lifecycle' +import { archiveWorkspaceInTransaction, finishWorkspaceArchive } from '@/lib/workspaces/lifecycle' + +/** All durable archive state commits together; external notifications follow the commit. */ +export async function archiveProjectInTransaction(tx: DbTransaction, projectId: string) { + await lockProject(tx, projectId) + const [record] = await tx.select().from(project).where(eq(project.id, projectId)) + if (!record) throw new OrchestrationError('not_found', 'Project not found') + const now = record.archivedAt ?? new Date() + const members = await tx + .select({ id: projectWorkspace.workspaceId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.projectId, projectId)) + .orderBy(asc(projectWorkspace.workspaceId)) + const workflows: { id: string; workspaceId: string; serverIds: string[] }[] = [] + const environments: { id: string; serverIds: string[] }[] = [] + for (const { id: workspaceId } of members) { + await tx + .select({ id: workspace.id }) + .from(workspace) + .where(eq(workspace.id, workspaceId)) + .for('update') + const rows = await tx + .select({ id: workflow.id }) + .from(workflow) + .where(and(eq(workflow.workspaceId, workspaceId), isNull(workflow.archivedAt))) + .orderBy(asc(workflow.id)) + for (const row of rows) { + const servers = await tx + .select({ id: workflowMcpTool.serverId }) + .from(workflowMcpTool) + .where(eq(workflowMcpTool.workflowId, row.id)) + await archiveWorkflowInTransaction(tx, row.id, now) + workflows.push({ id: row.id, workspaceId, serverIds: servers.map((server) => server.id) }) + } + const servers = await tx + .select({ id: workflowMcpServer.id }) + .from(workflowMcpServer) + .where(eq(workflowMcpServer.workspaceId, workspaceId)) + await archiveWorkspaceInTransaction(tx, workspaceId, now) + environments.push({ id: workspaceId, serverIds: servers.map((server) => server.id) }) + } + await tx.update(project).set({ archivedAt: now, updatedAt: now }).where(eq(project.id, projectId)) + return { workflows, environments } +} + +export async function finishProjectArchive( + effects: Awaited>, + requestId: string +): Promise { + for (const row of effects.workflows) + await finishWorkflowArchive(row.id, row.workspaceId, row.serverIds, { requestId }) + for (const row of effects.environments) await finishWorkspaceArchive(row.id, row.serverIds) +} diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts new file mode 100644 index 00000000000..7a328d71702 --- /dev/null +++ b/apps/sim/lib/projects/membership.ts @@ -0,0 +1,238 @@ +import { permissionGroup, project, projectWorkspace, workspace } from '@sim/db/schema' +import { getPostgresErrorCode } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { and, asc, eq, inArray, isNull, ne, notInArray, sql } from 'drizzle-orm' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' + +/** Canonical Project mutex; membership and lifecycle writers hold it until commit. */ +export async function lockProject(tx: DbTransaction, projectId: string): Promise { + await tx.execute(sql`SELECT set_config('lock_timeout', '5000ms', true)`) + try { + await tx.execute( + sql`SELECT pg_advisory_xact_lock(hashtextextended(${`project:${projectId}`}, 0))` + ) + } catch (error) { + if (getPostgresErrorCode(error) === '55P03') + throw new OrchestrationError('conflict', 'Project is changing; retry the operation') + throw error + } +} + +function generatedProjectName(workspaceName: string): string { + const suffix = ' - Project' + return `${(workspaceName.trim() || 'Untitled').slice(0, 100 - suffix.length)}${suffix}` +} + +export async function createProjectForWorkspace( + tx: DbTransaction, + input: { + workspaceId: string + name: string + organizationId: string | null + ownerId: string + archivedAt?: Date | null + } +): Promise { + const id = generateId() + await tx.insert(project).values({ + id, + name: generatedProjectName(input.name), + organizationId: input.organizationId, + ownerId: input.ownerId, + archivedAt: input.archivedAt ?? null, + }) + await tx.insert(projectWorkspace).values({ projectId: id, workspaceId: input.workspaceId }) + return id +} + +/** Returns null only for a legacy workspace awaiting the operator-run backfill. */ +export async function lockWorkspaceProject(tx: DbTransaction, workspaceId: string) { + const [membership] = await tx + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, workspaceId)) + .limit(1) + if (!membership) return null + await lockProject(tx, membership.projectId) + const [current] = await tx + .select({ project }) + .from(projectWorkspace) + .innerJoin(project, eq(project.id, projectWorkspace.projectId)) + .where(eq(projectWorkspace.workspaceId, workspaceId)) + .limit(1) + if (!current || current.project.id !== membership.projectId) { + throw new OrchestrationError('conflict', 'Project membership changed; retry the operation') + } + return current.project +} + +export async function requireForkProject(tx: DbTransaction, parentWorkspaceId: string) { + const parent = await lockWorkspaceProject(tx, parentWorkspaceId) + if (!parent) + throw new OrchestrationError( + 'conflict', + 'This workspace needs the Project backfill before it can be forked' + ) + if (parent.archivedAt) throw new OrchestrationError('conflict', 'Cannot fork an archived Project') + return parent +} + +/** Individual removal cannot leave an active Project without an active environment. */ +export async function requireRemainingProjectEnvironment( + tx: DbTransaction, + workspaceId: string +): Promise { + const owner = await lockWorkspaceProject(tx, workspaceId) + if (!owner) return + const [remaining] = await tx + .select({ id: workspace.id }) + .from(projectWorkspace) + .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) + .where( + and( + eq(projectWorkspace.projectId, owner.id), + ne(workspace.id, workspaceId), + isNull(workspace.archivedAt) + ) + ) + .limit(1) + if (!remaining && !owner.archivedAt) { + throw new OrchestrationError( + 'conflict', + 'The last active environment cannot be removed. Archive the Project instead.' + ) + } +} + +/** Called before clearing the edge, under the existing lineage lock. */ +export async function splitForkProject( + tx: DbTransaction, + workspaceId: string +): Promise { + const owner = await lockWorkspaceProject(tx, workspaceId) + if (!owner) return null + if (owner.archivedAt) + throw new OrchestrationError('conflict', 'Cannot disconnect an archived Project') + const rows = await tx.execute<{ + id: string + name: string + owner_id: string + archived_at: Date | null + project_id: string | null + }>(sql` + WITH RECURSIVE descendants AS ( + SELECT id, name, owner_id, archived_at FROM workspace WHERE id = ${workspaceId} + UNION + SELECT w.id, w.name, w.owner_id, w.archived_at FROM workspace w JOIN descendants d ON w.forked_from_workspace_id = d.id + ) SELECT d.*, pw.project_id FROM descendants d LEFT JOIN project_workspace pw ON pw.workspace_id = d.id + `) + if (rows.some((row) => row.project_id !== owner.id)) + throw new OrchestrationError( + 'conflict', + 'Fork descendants need Project membership reconciliation before disconnecting' + ) + const root = rows.find((row) => row.id === workspaceId) + if (!root || rows.every((row) => row.archived_at)) + throw new OrchestrationError('conflict', 'A new Project needs an active environment') + const ids = rows.map((row) => row.id) + const [remaining] = await tx + .select({ id: workspace.id }) + .from(projectWorkspace) + .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) + .where( + and( + eq(projectWorkspace.projectId, owner.id), + isNull(workspace.archivedAt), + notInArray(workspace.id, ids) + ) + ) + .limit(1) + if (!remaining) + throw new OrchestrationError( + 'conflict', + 'Disconnecting would remove the last active environment from this Project' + ) + const id = generateId() + await tx.insert(project).values({ + id, + name: generatedProjectName(root.name), + organizationId: owner.organizationId, + ownerId: root.owner_id, + }) + await tx + .update(projectWorkspace) + .set({ projectId: id }) + .where( + and(eq(projectWorkspace.projectId, owner.id), inArray(projectWorkspace.workspaceId, ids)) + ) + if (owner.organizationId) { + await tx.execute(sql` + UPDATE ${permissionGroup} + SET config = jsonb_set(config, '{deniedPartialAccessProjectIssues}', + (config->'deniedPartialAccessProjectIssues') || to_jsonb(${id}::text)), updated_at = now() + WHERE organization_id = ${owner.organizationId} + AND config->'deniedPartialAccessProjectIssues' ? ${owner.id} + `) + } + return id +} + +/** Ownership changes include the complete Project; never silently split Project-wide resources. */ +export async function transferWorkspaceProjects( + tx: DbTransaction, + workspaceIds: string[], + organizationId: string | null, + ownerId?: string +): Promise { + if (!workspaceIds.length) return + const owners = await tx + .selectDistinct({ id: projectWorkspace.projectId }) + .from(projectWorkspace) + .where(inArray(projectWorkspace.workspaceId, workspaceIds)) + .orderBy(asc(projectWorkspace.projectId)) + const selected = new Set(workspaceIds) + for (const owner of owners) { + await tryLockProject(tx, owner.id) + const members = await tx + .select({ id: projectWorkspace.workspaceId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.projectId, owner.id)) + if (members.some((row) => !selected.has(row.id))) { + throw new OrchestrationError( + 'conflict', + 'Move all environments in the Project together, or disconnect the fork first' + ) + } + const [current] = await tx + .select({ organizationId: project.organizationId }) + .from(project) + .where(eq(project.id, owner.id)) + if (current?.organizationId && current.organizationId !== organizationId) { + await tx.execute(sql` + UPDATE ${permissionGroup} + SET config = jsonb_set(config, '{deniedPartialAccessProjectIssues}', + (config->'deniedPartialAccessProjectIssues') - ${owner.id}), updated_at = now() + WHERE organization_id = ${current.organizationId} + AND config->'deniedPartialAccessProjectIssues' ? ${owner.id} + `) + } + await tx + .update(project) + .set({ + organizationId, + ownerId, + updatedAt: new Date(), + }) + .where(eq(project.id, owner.id)) + } +} + +/** Existing ownership paths can hold workspace rows first; refuse contention instead of inverting locks. */ +export async function tryLockProject(tx: DbOrTx, projectId: string): Promise { + const [lock] = await tx.execute<{ acquired: boolean }>( + sql`SELECT pg_try_advisory_xact_lock(hashtextextended(${`project:${projectId}`}, 0)) AS acquired` + ) + if (!lock?.acquired) + throw new OrchestrationError('conflict', 'Project is changing; retry the ownership change') +} diff --git a/apps/sim/lib/users/account-deletion.ts b/apps/sim/lib/users/account-deletion.ts index b9876209500..57f41ff7e79 100644 --- a/apps/sim/lib/users/account-deletion.ts +++ b/apps/sim/lib/users/account-deletion.ts @@ -25,6 +25,7 @@ import type { import { getHighestPriorityPersonalSubscription } from '@/lib/billing/core/plan' import { isSoleOwnerOfPaidOrganization } from '@/lib/billing/organizations/membership' import { OrchestrationError } from '@/lib/core/orchestration/types' +import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' import { appendTableEvent, type TableEvent } from '@/lib/table/events' import { type CancelledCellMarker, @@ -681,6 +682,7 @@ export async function deleteUserAccount(userId: string): Promise { + await prepareProjectsForAccountDeletion(tx, userId, doomedWorkspaceIds) if (doomedWorkspaceIds.length > 0) { /** * Re-checked here rather than trusted from the plan: a workspace that diff --git a/apps/sim/lib/workflows/lifecycle.ts b/apps/sim/lib/workflows/lifecycle.ts index 965ae9ca62b..27e59772f40 100644 --- a/apps/sim/lib/workflows/lifecycle.ts +++ b/apps/sim/lib/workflows/lifecycle.ts @@ -3,6 +3,7 @@ import { apiKey, chat, folder as folderTable, + projectWorkspace, webhook, workflow, workflowDeploymentVersion, @@ -16,6 +17,7 @@ import { env } from '@/lib/core/config/env' import { PlatformEvents } from '@/lib/core/telemetry' import { generateRequestId } from '@/lib/core/utils/request' import { getSocketServerUrl } from '@/lib/core/utils/urls' +import type { DbTransaction } from '@/lib/db/types' import { mcpPubSub } from '@/lib/mcp/pubsub' import { releaseWebhookPathClaims } from '@/lib/webhooks/path-claims' import { supersedeInFlightDeploymentOperations } from '@/lib/workflows/persistence/deployment-operations' @@ -106,87 +108,14 @@ export async function archiveWorkflow( .from(workflowMcpTool) .where(and(eq(workflowMcpTool.workflowId, workflowId), isNull(workflowMcpTool.archivedAt))) - await db.transaction(async (tx) => { - await supersedeInFlightDeploymentOperations(tx, workflowId) - await releaseWebhookPathClaims(tx, workflowId) - - await tx - .update(workflowSchedule) - .set({ - archivedAt: now, - updatedAt: now, - status: 'disabled', - nextRunAt: null, - lastQueuedAt: null, - }) - .where(and(eq(workflowSchedule.workflowId, workflowId), isNull(workflowSchedule.archivedAt))) - - await tx - .update(webhook) - .set({ - archivedAt: now, - updatedAt: now, - isActive: false, - }) - .where(and(eq(webhook.workflowId, workflowId), isNull(webhook.archivedAt))) - - await tx - .update(chat) - .set({ - archivedAt: now, - updatedAt: now, - isActive: false, - }) - .where(and(eq(chat.workflowId, workflowId), isNull(chat.archivedAt))) - - await tx - .update(workflowMcpTool) - .set({ - archivedAt: now, - updatedAt: now, - }) - .where(and(eq(workflowMcpTool.workflowId, workflowId), isNull(workflowMcpTool.archivedAt))) - - await tx - .update(workflowDeploymentVersion) - .set({ - isActive: false, - }) - .where(eq(workflowDeploymentVersion.workflowId, workflowId)) - - await tx - .update(workflow) - .set({ - archivedAt: now, - updatedAt: now, - isDeployed: false, - isPublicApi: false, - }) - .where(and(eq(workflow.id, workflowId), isNull(workflow.archivedAt))) - }) - - try { - PlatformEvents.workflowDeleted({ - workflowId, - workspaceId: existingWorkflow.workspaceId || undefined, - }) - } catch {} - - if (options.notifySocket !== false) { - await notifyWorkflowArchived(workflowId, options.requestId) - } - - await cleanupExternalWebhooksForWorkflow(workflowId, options.requestId) + await db.transaction((tx) => archiveWorkflowInTransaction(tx, workflowId, now)) - if (existingWorkflow.workspaceId && mcpPubSub && affectedWorkflowMcpServers.length > 0) { - const uniqueServerIds = [...new Set(affectedWorkflowMcpServers.map((row) => row.serverId))] - for (const serverId of uniqueServerIds) { - mcpPubSub.publishWorkflowToolsChanged({ - serverId, - workspaceId: existingWorkflow.workspaceId, - }) - } - } + await finishWorkflowArchive( + workflowId, + existingWorkflow.workspaceId, + affectedWorkflowMcpServers.map((row) => row.serverId), + options + ) return { archived: true, @@ -368,12 +297,131 @@ export async function disableUserResources(userId: string): Promise { .from(workspace) .where(and(eq(workspace.ownerId, userId), isNull(workspace.archivedAt))) - await Promise.all([ - ...ownedWorkspaces.map((w) => archiveWorkspace(w.id, { requestId })), - db.delete(apiKey).where(eq(apiKey.userId, userId)), - ]) + const { archiveProjectInTransaction, finishProjectArchive } = await import( + '@/lib/projects/lifecycle' + ) + const { lockWorkspaceProject } = await import('@/lib/projects/membership') + const owned = new Set(ownedWorkspaces.map((row) => row.id)) + const processed = new Set() + for (const row of ownedWorkspaces) { + if (processed.has(row.id)) continue + const archived = await db.transaction(async (tx) => { + const record = await lockWorkspaceProject(tx, row.id) + if (!record) return null + const active = await tx + .select({ id: workspace.id }) + .from(projectWorkspace) + .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) + .where(and(eq(projectWorkspace.projectId, record.id), isNull(workspace.archivedAt))) + if (!active.every((entry) => owned.has(entry.id))) return null + return archiveProjectInTransaction(tx, record.id) + }) + if (archived) { + for (const entry of archived.environments) processed.add(entry.id) + await finishProjectArchive(archived, requestId) + } else { + await archiveWorkspace(row.id, { requestId }) + processed.add(row.id) + } + } + await db.delete(apiKey).where(eq(apiKey.userId, userId)) logger.info( `[${requestId}] Disabled resources for user ${userId}: archived ${ownedWorkspaces.length} workspaces, deleted API keys` ) } + +/** Durable archive state shared by single-workflow and compound Project archival. */ +export async function archiveWorkflowInTransaction( + tx: DbTransaction, + workflowId: string, + now: Date +): Promise { + await supersedeInFlightDeploymentOperations(tx, workflowId) + await releaseWebhookPathClaims(tx, workflowId) + + await tx + .update(workflowSchedule) + .set({ + archivedAt: now, + updatedAt: now, + status: 'disabled', + nextRunAt: null, + lastQueuedAt: null, + }) + .where(and(eq(workflowSchedule.workflowId, workflowId), isNull(workflowSchedule.archivedAt))) + + await tx + .update(webhook) + .set({ + archivedAt: now, + updatedAt: now, + isActive: false, + }) + .where(and(eq(webhook.workflowId, workflowId), isNull(webhook.archivedAt))) + + await tx + .update(chat) + .set({ + archivedAt: now, + updatedAt: now, + isActive: false, + }) + .where(and(eq(chat.workflowId, workflowId), isNull(chat.archivedAt))) + + await tx + .update(workflowMcpTool) + .set({ + archivedAt: now, + updatedAt: now, + }) + .where(and(eq(workflowMcpTool.workflowId, workflowId), isNull(workflowMcpTool.archivedAt))) + + await tx + .update(workflowDeploymentVersion) + .set({ + isActive: false, + }) + .where(eq(workflowDeploymentVersion.workflowId, workflowId)) + + await tx + .update(workflow) + .set({ + archivedAt: now, + updatedAt: now, + isDeployed: false, + isPublicApi: false, + }) + .where(and(eq(workflow.id, workflowId), isNull(workflow.archivedAt))) +} + +/** Best-effort external notifications run only after durable archive state commits. */ +export async function finishWorkflowArchive( + workflowId: string, + workspaceId: string | null, + serverIds: string[], + options: ArchiveWorkflowOptions +): Promise { + try { + PlatformEvents.workflowDeleted({ + workflowId, + workspaceId: workspaceId || undefined, + }) + } catch {} + + if (options.notifySocket !== false) { + await notifyWorkflowArchived(workflowId, options.requestId) + } + + await cleanupExternalWebhooksForWorkflow(workflowId, options.requestId) + + if (workspaceId && mcpPubSub && serverIds.length > 0) { + const uniqueServerIds = [...new Set(serverIds)] + for (const serverId of uniqueServerIds) { + mcpPubSub.publishWorkflowToolsChanged({ + serverId, + workspaceId: workspaceId, + }) + } + } +} diff --git a/apps/sim/lib/workflows/orchestration/workflow-lifecycle.test.ts b/apps/sim/lib/workflows/orchestration/workflow-lifecycle.test.ts index b8eef76dee3..c5ed40c2550 100644 --- a/apps/sim/lib/workflows/orchestration/workflow-lifecycle.test.ts +++ b/apps/sim/lib/workflows/orchestration/workflow-lifecycle.test.ts @@ -2,7 +2,9 @@ import { auditMock, dbChainMockFns, posthogServerMock, + queueTableRows, resetDbChainMock, + schemaMock, workflowAuthzMockFns, workflowsPersistenceUtilsMock, workflowsPersistenceUtilsMockFns, @@ -45,6 +47,9 @@ const createParams = { describe('performCreateWorkflowTransition unique-violation handling', () => { beforeEach(() => { resetDbChainMock() + queueTableRows(schemaMock.workspace, [ + { archivedAt: null, forkSyncNewWorkflowsExcluded: false }, + ]) workflowAuthzMockFns.mockIsFolderInWorkspace.mockResolvedValue(true) workflowsPersistenceUtilsMockFns.mockSaveWorkflowToNormalizedTables.mockResolvedValue({ success: true, diff --git a/apps/sim/lib/workflows/persistence/duplicate.test.ts b/apps/sim/lib/workflows/persistence/duplicate.test.ts index 7a6706e9b46..d9a2275bf50 100644 --- a/apps/sim/lib/workflows/persistence/duplicate.test.ts +++ b/apps/sim/lib/workflows/persistence/duplicate.test.ts @@ -51,6 +51,9 @@ function insertedValuesFor(table: unknown): unknown[] { describe('duplicateWorkflow ordering', () => { beforeEach(() => { resetDbChainMock() + queueTableRows(schemaMock.workspace, [ + { archivedAt: null, forkSyncNewWorkflowsExcluded: false }, + ]) vi.stubGlobal('crypto', { randomUUID: vi.fn().mockReturnValue('new-workflow-id'), diff --git a/apps/sim/lib/workflows/persistence/new-workflow-row.ts b/apps/sim/lib/workflows/persistence/new-workflow-row.ts index e1e10809137..36091a228f8 100644 --- a/apps/sim/lib/workflows/persistence/new-workflow-row.ts +++ b/apps/sim/lib/workflows/persistence/new-workflow-row.ts @@ -1,6 +1,7 @@ import { type workflow, workspace } from '@sim/db/schema' import { and, eq, isNull } from 'drizzle-orm' import type { DbOrTx } from '@/lib/db/types' +import { lockActiveWorkspace } from '@/lib/workspaces/active-workspace' interface NewWorkflowRowInput { id: string @@ -40,6 +41,7 @@ export async function readForkSyncNewWorkflowsExcluded( * `copyWorkflowStateIntoTarget` instead. */ export async function buildNewWorkflowRow(executor: DbOrTx, input: NewWorkflowRowInput) { + const workspace = await lockActiveWorkspace(executor, input.workspaceId) const now = input.now ?? new Date() return { id: input.id, @@ -55,6 +57,6 @@ export async function buildNewWorkflowRow(executor: DbOrTx, input: NewWorkflowRo isDeployed: false, runCount: 0, variables: input.variables ?? {}, - forkSyncExcluded: await readForkSyncNewWorkflowsExcluded(executor, input.workspaceId), + forkSyncExcluded: workspace.forkSyncNewWorkflowsExcluded, } satisfies typeof workflow.$inferInsert } diff --git a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts index bfd53dbe599..1b30b3d4a3f 100644 --- a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts +++ b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts @@ -8,6 +8,8 @@ import { knowledgeBase, outboxEvent, permissions, + project, + projectWorkspace, user, userTableDefinitions, workflow, @@ -25,6 +27,7 @@ import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' import { withWorkspaceInvocationScope } from '@/lib/core/application/workspace-invocation-scope' import { processOutboxEventById } from '@/lib/core/outbox/service' import { createScopedCliTransport } from '@/lib/mothership/agent-cli/scoped-transport' +import { createProjectForWorkspace } from '@/lib/projects/membership' import { workflowDeploymentOutboxHandlers } from '@/lib/workflows/deployment-outbox' import { performCreateWorkflowTransition } from '@/lib/workflows/orchestration/workflow-lifecycle' import { duplicateWorkflow } from '@/lib/workflows/persistence/duplicate' @@ -137,6 +140,14 @@ describe('authorized fork and sync against PostgreSQL', () => { billedAccountUserId: userId, allowPersonalApiKeys: true, }) + await db.transaction((tx) => + createProjectForWorkspace(tx, { + workspaceId: sourceWorkspaceId, + name: 'Fork source fixture', + ownerId: userId, + organizationId: null, + }) + ) await db.insert(permissions).values({ id: generateId(), userId, @@ -169,6 +180,19 @@ describe('authorized fork and sync against PostgreSQL', () => { }) }) afterAll(async () => { + const owned = await db + .select({ id: project.id }) + .from(project) + .where(eq(project.ownerId, userId)) + if (owned.length) { + await db.delete(projectWorkspace).where( + inArray( + projectWorkspace.projectId, + owned.map((row) => row.id) + ) + ) + await db.delete(project).where(eq(project.ownerId, userId)) + } for (const id of createdWorkspaceIds) await db.delete(workspace).where(eq(workspace.id, id)) await db.delete(workspace).where(eq(workspace.id, sourceWorkspaceId)) await db.delete(user).where(eq(user.id, userId)) diff --git a/apps/sim/lib/workspaces/active-workspace.ts b/apps/sim/lib/workspaces/active-workspace.ts new file mode 100644 index 00000000000..f506aa02ae8 --- /dev/null +++ b/apps/sim/lib/workspaces/active-workspace.ts @@ -0,0 +1,19 @@ +import { workspace } from '@sim/db/schema' +import { eq } from 'drizzle-orm' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { DbOrTx } from '@/lib/db/types' + +/** Transactional resource creation holds this row through insertion so archival cannot overtake it. */ +export async function lockActiveWorkspace(executor: DbOrTx, workspaceId: string) { + const [record] = await executor + .select({ + archivedAt: workspace.archivedAt, + forkSyncNewWorkflowsExcluded: workspace.forkSyncNewWorkflowsExcluded, + }) + .from(workspace) + .where(eq(workspace.id, workspaceId)) + .for('share') + .limit(1) + if (!record || record.archivedAt) throw new OrchestrationError('not_found', 'Workspace not found') + return record +} diff --git a/apps/sim/lib/workspaces/admin-move.ts b/apps/sim/lib/workspaces/admin-move.ts index 1571faf8af3..662ef5807ec 100644 --- a/apps/sim/lib/workspaces/admin-move.ts +++ b/apps/sim/lib/workspaces/admin-move.ts @@ -41,6 +41,7 @@ import type { DbOrTx } from '@/lib/db/types' import { getInvitationById, isInvitationExpired } from '@/lib/invitations/core' import { acquireInvitationMutationLocks } from '@/lib/invitations/locks' import { PENDING_INVITATION_UNIQUE_INDEX, sendInvitationEmail } from '@/lib/invitations/send' +import { transferWorkspaceProjects } from '@/lib/projects/membership' import { invalidateWorkspaceTableLimitsCache } from '@/lib/table/billing' import { deleteCustomBlock } from '@/lib/workflows/custom-blocks/operations' import { @@ -1356,6 +1357,8 @@ export async function moveWorkspaceToOrganization(params: { }) : { detachedPermissionGroupIds: [] } + await transferWorkspaceProjects(tx, [params.workspaceId], params.destinationOrganizationId) + await changeWorkspaceStoragePayerInTx(tx, { workspaceId: params.workspaceId, organizationId: params.destinationOrganizationId, diff --git a/apps/sim/lib/workspaces/create.test.ts b/apps/sim/lib/workspaces/create.test.ts index fc42089f009..680a6fd1883 100644 --- a/apps/sim/lib/workspaces/create.test.ts +++ b/apps/sim/lib/workspaces/create.test.ts @@ -1,6 +1,8 @@ +import { workspace } from '@sim/db/schema' import { dbChainMock, dbChainMockFns, + queueTableRows, resetDbChainMock, workflowsPersistenceUtilsMock, } from '@sim/testing' @@ -122,6 +124,7 @@ describe('createDefaultPersonalWorkspaceInTransaction', () => { */ it('creates an ungoverned personal workspace and resolves no regime', async () => { mockLockWorkspaceCreationContext.mockResolvedValue({ billedAccountUserId: 'user-1' }) + queueTableRows(workspace, [{ archivedAt: null, forkSyncNewWorkflowsExcluded: false }]) const tx = dbChainMock.db as unknown as DbOrTx await createDefaultPersonalWorkspaceInTransaction(tx, { diff --git a/apps/sim/lib/workspaces/create.ts b/apps/sim/lib/workspaces/create.ts index 8abfb8d2c99..b8aea562d5a 100644 --- a/apps/sim/lib/workspaces/create.ts +++ b/apps/sim/lib/workspaces/create.ts @@ -5,6 +5,7 @@ import { getPostgresConstraintName, getPostgresErrorCode } from '@sim/utils/erro import { generateId } from '@sim/utils/id' import { PlatformEvents } from '@/lib/core/telemetry' import type { DbTransaction } from '@/lib/db/types' +import { createProjectForWorkspace } from '@/lib/projects/membership' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' @@ -129,6 +130,13 @@ export async function createWorkspaceInTransaction( updatedAt: now, }) + await createProjectForWorkspace(tx, { + workspaceId, + name, + organizationId: organizationId ?? null, + ownerId: userId, + }) + const permissionRows = [ { id: generateId(), diff --git a/apps/sim/lib/workspaces/lifecycle.test.ts b/apps/sim/lib/workspaces/lifecycle.test.ts deleted file mode 100644 index 5d447ae689b..00000000000 --- a/apps/sim/lib/workspaces/lifecycle.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { - dbChainMockFns, - permissionsMock, - permissionsMockFns, - queueTableRows, - resetDbChainMock, - schemaMock, -} from '@sim/testing' -import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' - -const { mockArchiveWorkflowsForWorkspace } = vi.hoisted(() => ({ - mockArchiveWorkflowsForWorkspace: vi.fn(), -})) - -const mockGetWorkspaceWithOwner = permissionsMockFns.mockGetWorkspaceWithOwner - -vi.mock('@/lib/workflows/lifecycle', () => ({ - archiveWorkflowsForWorkspace: (...args: unknown[]) => mockArchiveWorkflowsForWorkspace(...args), -})) - -vi.mock('@/lib/workspaces/permissions/utils', () => permissionsMock) - -import { archiveWorkspace } from '@/lib/workspaces/lifecycle' - -function createUpdateChain() { - return { - set: vi.fn().mockReturnValue({ - where: vi.fn().mockResolvedValue([]), - }), - } -} - -describe('workspace lifecycle', () => { - beforeEach(() => { - resetDbChainMock() - }) - - afterAll(() => { - resetDbChainMock() - }) - - it('archives workspace and dependent resources', async () => { - mockGetWorkspaceWithOwner.mockResolvedValue({ - id: 'workspace-1', - name: 'Workspace 1', - ownerId: 'user-1', - archivedAt: null, - }) - mockArchiveWorkflowsForWorkspace.mockResolvedValue(2) - queueTableRows(schemaMock.workflowMcpServer, [{ id: 'server-1' }]) - - const tx = { - select: vi.fn().mockReturnValue({ - from: vi.fn().mockReturnValue({ - where: vi.fn().mockResolvedValue([{ id: 'kb-1' }]), - }), - }), - update: vi.fn().mockImplementation(() => createUpdateChain()), - delete: vi.fn().mockImplementation(() => ({ - where: vi.fn().mockResolvedValue([]), - })), - } - dbChainMockFns.transaction.mockImplementation( - async (callback: (trx: typeof tx) => Promise) => callback(tx) - ) - - const result = await archiveWorkspace('workspace-1', { requestId: 'req-1' }) - - expect(result).toEqual({ - archived: true, - workspaceName: 'Workspace 1', - }) - expect(mockArchiveWorkflowsForWorkspace).toHaveBeenCalledWith('workspace-1', { - requestId: 'req-1', - }) - expect(tx.update).toHaveBeenCalledTimes(9) - expect(tx.delete).toHaveBeenCalledTimes(1) - }) - - it('retries child cleanup for already archived workspaces', async () => { - mockGetWorkspaceWithOwner.mockResolvedValue({ - id: 'workspace-1', - name: 'Workspace 1', - ownerId: 'user-1', - archivedAt: new Date(), - }) - - const result = await archiveWorkspace('workspace-1', { requestId: 'req-1' }) - - expect(result).toEqual({ - archived: false, - workspaceName: 'Workspace 1', - }) - expect(mockArchiveWorkflowsForWorkspace).toHaveBeenCalledWith('workspace-1', { - requestId: 'req-1', - }) - expect(dbChainMockFns.transaction).toHaveBeenCalledOnce() - }) -}) diff --git a/apps/sim/lib/workspaces/lifecycle.ts b/apps/sim/lib/workspaces/lifecycle.ts index e66f5465644..078af37507d 100644 --- a/apps/sim/lib/workspaces/lifecycle.ts +++ b/apps/sim/lib/workspaces/lifecycle.ts @@ -14,8 +14,10 @@ import { } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { and, eq, inArray, isNull, sql } from 'drizzle-orm' +import type { DbTransaction } from '@/lib/db/types' import { mcpPubSub } from '@/lib/mcp/pubsub' import { mcpService } from '@/lib/mcp/service' +import { requireRemainingProjectEnvironment } from '@/lib/projects/membership' import { archiveWorkflowsForWorkspace } from '@/lib/workflows/lifecycle' import { getWorkspaceWithOwner } from '@/lib/workspaces/permissions/utils' @@ -43,129 +45,145 @@ export async function archiveWorkspace( .where(eq(workflowMcpServer.workspaceId, workspaceId)) await db.transaction(async (tx) => { - await tx - .update(knowledgeBase) - .set({ - deletedAt: now, - updatedAt: now, - }) - .where(and(eq(knowledgeBase.workspaceId, workspaceId), isNull(knowledgeBase.deletedAt))) - - const workspaceKbIds = await tx - .select({ id: knowledgeBase.id }) - .from(knowledgeBase) - .where(eq(knowledgeBase.workspaceId, workspaceId)) - - const knowledgeBaseIds = workspaceKbIds.map((entry) => entry.id) - if (knowledgeBaseIds.length > 0) { - await tx - .update(document) - .set({ archivedAt: now }) - .where( - and( - inArray(document.knowledgeBaseId, knowledgeBaseIds), - isNull(document.archivedAt), - isNull(document.deletedAt) - ) - ) + await requireRemainingProjectEnvironment(tx, workspaceId) + await archiveWorkspaceInTransaction(tx, workspaceId, now) + }) - await tx - .update(knowledgeConnector) - .set({ archivedAt: now, status: 'paused', updatedAt: now }) - .where( - and( - inArray(knowledgeConnector.knowledgeBaseId, knowledgeBaseIds), - isNull(knowledgeConnector.archivedAt), - isNull(knowledgeConnector.deletedAt) - ) - ) - } + await archiveWorkflowsForWorkspace(workspaceId, options) + + logger.info(`[${options.requestId}] Archived workspace ${workspaceId}`) + + await finishWorkspaceArchive( + workspaceId, + workflowMcpServerIds.map((server) => server.id) + ) + + return { + archived: !workspaceRecord.archivedAt, + workspaceName: workspaceRecord.name, + } +} +/** Durable environment archive changes; callers own Project minimum-environment checks. */ +export async function archiveWorkspaceInTransaction( + tx: DbTransaction, + workspaceId: string, + now: Date +): Promise { + await tx + .update(knowledgeBase) + .set({ + deletedAt: now, + updatedAt: now, + }) + .where(and(eq(knowledgeBase.workspaceId, workspaceId), isNull(knowledgeBase.deletedAt))) + + const workspaceKbIds = await tx + .select({ id: knowledgeBase.id }) + .from(knowledgeBase) + .where(eq(knowledgeBase.workspaceId, workspaceId)) + + const knowledgeBaseIds = workspaceKbIds.map((entry) => entry.id) + if (knowledgeBaseIds.length > 0) { await tx - .update(userTableDefinitions) - .set({ - archivedAt: now, - updatedAt: now, - }) + .update(document) + .set({ archivedAt: now }) .where( and( - eq(userTableDefinitions.workspaceId, workspaceId), - isNull(userTableDefinitions.archivedAt) + inArray(document.knowledgeBaseId, knowledgeBaseIds), + isNull(document.archivedAt), + isNull(document.deletedAt) ) ) await tx - .update(workspaceFiles) - .set({ - deletedAt: now, - }) - .where(and(eq(workspaceFiles.workspaceId, workspaceId), isNull(workspaceFiles.deletedAt))) - - await tx - .update(invitation) - .set({ - status: 'cancelled', - updatedAt: now, - }) + .update(knowledgeConnector) + .set({ archivedAt: now, status: 'paused', updatedAt: now }) .where( and( - eq(invitation.status, 'pending'), - sql`${invitation.id} IN ( + inArray(knowledgeConnector.knowledgeBaseId, knowledgeBaseIds), + isNull(knowledgeConnector.archivedAt), + isNull(knowledgeConnector.deletedAt) + ) + ) + } + + await tx + .update(userTableDefinitions) + .set({ + archivedAt: now, + updatedAt: now, + }) + .where( + and( + eq(userTableDefinitions.workspaceId, workspaceId), + isNull(userTableDefinitions.archivedAt) + ) + ) + + await tx + .update(workspaceFiles) + .set({ + deletedAt: now, + }) + .where(and(eq(workspaceFiles.workspaceId, workspaceId), isNull(workspaceFiles.deletedAt))) + + await tx + .update(invitation) + .set({ + status: 'cancelled', + updatedAt: now, + }) + .where( + and( + eq(invitation.status, 'pending'), + sql`${invitation.id} IN ( SELECT ${invitationWorkspaceGrant.invitationId} FROM ${invitationWorkspaceGrant} WHERE ${invitationWorkspaceGrant.workspaceId} = ${workspaceId} )` - ) ) + ) + + await tx + .delete(apiKey) + .where(and(eq(apiKey.workspaceId, workspaceId), eq(apiKey.type, 'workspace'))) + + await tx + .update(workflowMcpServer) + .set({ + deletedAt: now, + isPublic: false, + updatedAt: now, + }) + .where(eq(workflowMcpServer.workspaceId, workspaceId)) - await tx - .delete(apiKey) - .where(and(eq(apiKey.workspaceId, workspaceId), eq(apiKey.type, 'workspace'))) - - await tx - .update(workflowMcpServer) - .set({ - deletedAt: now, - isPublic: false, - updatedAt: now, - }) - .where(eq(workflowMcpServer.workspaceId, workspaceId)) - - await tx - .update(mcpServers) - .set({ - deletedAt: now, - enabled: false, - updatedAt: now, - }) - .where(and(eq(mcpServers.workspaceId, workspaceId), isNull(mcpServers.deletedAt))) - - await tx - .update(workspace) - .set({ - archivedAt: now, - updatedAt: now, - }) - .where(and(eq(workspace.id, workspaceId), isNull(workspace.archivedAt))) - }) - - await archiveWorkflowsForWorkspace(workspaceId, options) - - logger.info(`[${options.requestId}] Archived workspace ${workspaceId}`) + await tx + .update(mcpServers) + .set({ + deletedAt: now, + enabled: false, + updatedAt: now, + }) + .where(and(eq(mcpServers.workspaceId, workspaceId), isNull(mcpServers.deletedAt))) + + await tx + .update(workspace) + .set({ + archivedAt: now, + updatedAt: now, + }) + .where(and(eq(workspace.id, workspaceId), isNull(workspace.archivedAt))) +} +/** Refreshes derived MCP state after the archive transaction commits. */ +export async function finishWorkspaceArchive( + workspaceId: string, + serverIds: string[] +): Promise { await mcpService.clearCache(workspaceId).catch(() => undefined) - - if (mcpPubSub && workflowMcpServerIds.length > 0) { - for (const server of workflowMcpServerIds) { - mcpPubSub.publishWorkflowToolsChanged({ - serverId: server.id, - workspaceId, - }) - } - } - - return { - archived: !workspaceRecord.archivedAt, - workspaceName: workspaceRecord.name, + if (mcpPubSub) { + for (const serverId of serverIds) + mcpPubSub.publishWorkflowToolsChanged({ serverId, workspaceId }) } } diff --git a/apps/sim/lib/workspaces/organization-workspaces.integration.ts b/apps/sim/lib/workspaces/organization-workspaces.integration.ts index b0fd0397d0a..331b66d33b8 100644 --- a/apps/sim/lib/workspaces/organization-workspaces.integration.ts +++ b/apps/sim/lib/workspaces/organization-workspaces.integration.ts @@ -26,6 +26,7 @@ const database = drizzle(connection, { schema }) beforeAll(async () => { await connection.unsafe(`CREATE SCHEMA "${schemaName}"`) await connection.unsafe(` + CREATE TABLE project_workspace (project_id text NOT NULL, workspace_id text UNIQUE NOT NULL); CREATE TABLE member (id text PRIMARY KEY, organization_id text, user_id text, role text); CREATE TABLE organization (id text PRIMARY KEY, storage_used_bytes bigint NOT NULL); CREATE TABLE invitation ( diff --git a/apps/sim/lib/workspaces/organization-workspaces.ts b/apps/sim/lib/workspaces/organization-workspaces.ts index 8659ab20424..2db7b69675d 100644 --- a/apps/sim/lib/workspaces/organization-workspaces.ts +++ b/apps/sim/lib/workspaces/organization-workspaces.ts @@ -14,6 +14,7 @@ import { changeWorkspaceStoragePayersInTx } from '@/lib/billing/storage/payer-tr import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { acquireInvitationMutationLocks } from '@/lib/invitations/locks' +import { transferWorkspaceProjects } from '@/lib/projects/membership' import { invalidateWorkspaceTableLimitsCache } from '@/lib/table/billing' import { getOrganizationOwnerId, WORKSPACE_MODE } from '@/lib/workspaces/policy' @@ -351,6 +352,7 @@ export async function attachOwnedWorkspacesToOrganizationTx( } } + await transferWorkspaceProjects(tx, ownedWorkspaceIds, organizationId) const now = new Date() await changeWorkspaceStoragePayersInTx( tx, @@ -492,6 +494,7 @@ export async function detachOrganizationWorkspacesTx( const workspaceIds = organizationWorkspaces .map((organizationWorkspace) => organizationWorkspace.id) .sort() + await transferWorkspaceProjects(tx, workspaceIds, null, organizationOwnerId ?? undefined) await lockWorkspaceRowsForPayerChanges(tx, workspaceIds) const payerChanges = organizationWorkspaces.map((organizationWorkspace) => ({ workspaceId: organizationWorkspace.id, diff --git a/docs/plans/project-backfill-runbook.md b/docs/plans/project-backfill-runbook.md new file mode 100644 index 00000000000..d5154830401 --- /dev/null +++ b/docs/plans/project-backfill-runbook.md @@ -0,0 +1,68 @@ +# Project backfill operations + +The SQL migration creates empty Project tables during the normal migration job. This backfill is a separate post-deployment operation. It is not registered in the startup migration registry: old replicas are still serving when that registry runs. + +## Operator execution + +Run the CLI from an approved maintenance host or a one-off migration container matching the deployed application revision. There is no dedicated GitHub Actions workflow and no automatic migration-registry entry for this backfill. The normal deployment pipeline still applies the additive SQL migration. + +Before apply: + +- Finish deploying the compatible release, including draining old workers. Pause manual lineage/ownership maintenance and coordinate a window without concurrent deployments. Without a workflow wrapper, the operator owns these checks; the CLI does not verify ECS cutover or serialize deployment pipelines. +- Supply `PROJECT_BACKFILL_DATABASE_URL` through the approved secret mechanism. Use a direct session-capable primary connection, with no pooled application DSN fallback. Verify the environment and the fingerprint printed by `identity`. +- Use a persistent, writable report directory. Retain the exact code revision, dry-run plan, apply reports, verification reports and console output with the release record. Reports contain resource metadata and should have restricted access. +- Rehearse staging using the same command before production. This task has not executed a hosted backfill or established an existing maintenance host. + +1. Run `dry-run` and inspect the JSON report: roots, environment IDs, planned Project IDs, proposed names, changes and conflicts. +2. Repair reported conflicts deliberately and produce a fresh dry run. Apply refuses incomplete or conflicted plans. +3. Run `apply --from-file --database-id --writers-drained`. Keep the same reviewed code revision and original plan for retries. The acknowledgement does not stop writers; each transaction applies one planned family after checking for changes. +4. Run `verify` independently. Require `completed: true`, `ready: true`, zero conflicts and zero missing assignments. Apply success alone does not establish readiness. +5. Repeat verification after normal writes resume. Keep Project/Issues consumers disabled until reconciliation and the separately deployed database membership/minimum-environment constraints are complete. This command never flips an activation flag. + +A canceled or failed apply may have committed earlier families. Retain its report and original dry-run artifact; reapply the same artifact after checking the failure. Existing matching planned IDs are reused. A changed code revision requires reviewing compatibility and normally a new dry-run plan. Never produce a new plan merely to hide an unexpected assignment. + +## Safety and failure handling + +- Dry-run and verify do not change database rows. All modes take a session advisory lock so two operator runs cannot overlap. The reserved connection and backend PID are checked before each batch; losing that connection stops the run. +- Discovery pages contain 100 workspace IDs. Each family defaults to at most 1,000 members at the CLI, with an explicit maximum of 10,000. Total discovery is capped at 250,000 workspaces; lineage ancestry is capped at 1,000 links. Oversized data needs a reviewed adjustment or separate migration, never an unbounded run. +- Apply takes `SHARE ROW EXCLUSIVE ... NOWAIT` locks on workspace, Project and membership tables before rediscovering the family. This excludes concurrent inserts/deletes/updates for the short transaction, including new descendants. Reads remain available. Busy writers cause immediate contention rather than an indefinitely queued lock. This is a maintenance operation: ordinary workspace writes can briefly wait while a batch holds its locks. +- A SHA-256 fingerprint covers the ordered environment records, including names, owners, organization, archive timestamps and fork connections. Any change since dry-run rejects that family. Existing Project assignments must match the planned Project ID. Existing names are retained. +- Reads retry transient connection/unavailability failures with bounded jitter. Writes retry only SQLSTATE `40001` (serialization), `40P01` (deadlock) and `55P03` (lock unavailable), at most three attempts. No connection-error write retry: the commit result may be unknown. Constraint errors, cancellation, statement timeouts and unknown errors stop the run. +- Lock timeout is 2 seconds; statements and transactions are capped at 30 seconds. PostgreSQL 17+ is required for the transaction timeout. Runtime is bounded between batches (default 10 minutes, maximum one hour), so the last transaction may finish after the budget. SIGINT/SIGTERM stop after the current transaction; forced termination rolls back an open transaction. +- Reports are replaced atomically before any write and after every committed family. Counts reflect acknowledged commits only. If the connection disappears at commit, counts can underreport; reconcile through the original plan and verification. A failed report write stops further batches; the last durable report remains available. Do not infer rollback from a missing final report. +- Reports distinguish planned counts in dry-run/verify from committed counts in apply. `completed` means the selected scan/plan was processed; `ready` is set only by successful verification. SQLSTATE is recorded without dumping database error payloads or credentials. + +## Conflict repair + +Report entries include the root/environment IDs, relevant Project IDs and reason. Examples: + +- Multiple Projects in one connected family: inspect intended lineage and retained data; repair membership or disconnect the intended subtree transactionally. Never automatically merge Projects. +- Cross-organization lineage: resolve the workspace ownership/lineage first using the approved organization lifecycle operation. +- Changed family after dry-run: inspect the change, then produce and review a new plan. +- Empty Project or archive mismatch: determine the intended retained environments and lifecycle state. Do not silently delete Project data or unarchive environments. +- Cycle or missing parent: repair the fork connection before migration. + +The runner does not repair anomalies on the operator's behalf. After repair, run dry-run and verify again. Membership uniqueness and foreign keys remain active throughout. + +## Self-hosted execution + +Use the migration image from the same release as the application, after all application/worker replicas run compatible code. PostgreSQL 17+ and a direct session-capable primary connection are required. Supply `PROJECT_BACKFILL_DATABASE_URL` through your deployment's secret mechanism; do not place it in shell history or command arguments. Mount a writable, persistent report directory owned by the container's non-root user. + +Inside that image, the working directory is `/app/packages/db`: + +```sh +bun --no-env-file scripts/backfill-projects.ts identity +PROJECT_BACKFILL_REPORT_PATH=/reports/plan.json \ + bun --no-env-file scripts/backfill-projects.ts dry-run +PROJECT_BACKFILL_REPORT_PATH=/reports/apply.json \ + bun --no-env-file scripts/backfill-projects.ts apply \ + --from-file /reports/plan.json --database-id --writers-drained +PROJECT_BACKFILL_REPORT_PATH=/reports/verify.json \ + bun --no-env-file scripts/backfill-projects.ts verify +``` + +Override the migration container command for this one-off operation; do not change the ordinary `db:migrate` startup command. Apply the same report review, writer coordination, replay and activation rules above. Keep the original plan on persistent storage until verification and the release are complete. + +## Local proof + +`packages/db/scripts/project-backfill.integration.ts` creates a separate disposable database for every case, seeds a pre-Project fixture and applies the actual `0393` SQL. It exercises real SQL and the CLI, including interruption, conflicting writers, lost sessions, report failures and idempotent replay. The normal integration workflow discovers this file and uploads the integration JSON result. Never aim this test at an existing app database; the shared test-infrastructure validator restricts the target to a local test database. diff --git a/docs/plans/project-entity-foundation.md b/docs/plans/project-entity-foundation.md new file mode 100644 index 00000000000..4126b389c63 --- /dev/null +++ b/docs/plans/project-entity-foundation.md @@ -0,0 +1,311 @@ +# Project entity foundation: implementation and rollout proposal + +Backend foundation implemented locally, with backfill hardening validated on 2026-10-01, following the approved planning review. Final feature review and hosted rollout remain separate. Database validation uses disposable test containers only. See the implementation and rollout notes below for the concrete scope. + +Migration `0393_project_foundation` follows `0392_dashboard`. The schema expansion and application writers belong in the foundation release; backfill and contract enforcement follow separately. + + +## Decisions and blockers + +Settled: Project and environment are separate identities; existing workspace IDs remain environment IDs. Issues belong to Project across environments. Project and environment names are independent. Workflow, table, file, knowledge and dashboard data remain environment scoped. Chat has no Project ownership or membership relation. Selecting a Project is navigation state, not execution authority. Ship backend/feature PRs before new UI. + +The user settled Project visibility and Issue access: any environment access makes the Project visible; the toggle lists only accessible environments. Issues are project-wide and all-or-nothing, available by default to anyone with access to any environment. Through permission groups, an org admin can disable all Issues for a Project for partial-access teammates (those lacking access to every environment in that Project). This replaces the earlier explicit Project-membership proposal for Issue access. + +1. **Access — settled:** derive navigation and default Issue access from current environment access. Apply the permission-group restriction only to partial-access teammates, for the target Project; no per-Issue or per-environment filtering of the Issue collection. Restricting Issues does not hide the Project or its accessible environments. Project rename/archive is allowed for org admins and workspace admins who administer every environment; Issue visibility alone does not grant administration. The partial-access Issue check excludes archived environments. +2. **Disconnect — settled:** disconnecting a fork moves it and all descendants into a newly created Project, atomically with unlinking the fork edge. The original Project retains its ID. Existing Issues stay in the original Project; the new Project starts empty. A user may explicitly ask Sim Chat/Mothership to migrate Issues later through an authorized operation, rather than coupling Issue migration to disconnect. +3. **Personal scope — settled:** Projects can exist without an organization. Organization deletion preserves each Project and all of its Issues together, retaining the Project ID and assigning an explicit personal owner; no Project/Issue cascade or replacement Project creation. + +Settled organization-deletion behavior: transfer each Project to an explicitly selected personal owner while retaining its ID and Issues, rather than splitting its Issues between former environment owners. Project archive cascades to every environment and workflow. An active Project must retain at least one active environment; users cannot individually delete/archive the last one. Whole-Project archive is the explicit exception, leaving an archived Project with archived environments, not an empty active Project. Cross-organization environment moves must explicitly separate the moved environment from the old Project or move the entire Project. Existing Issues stay with their original Project on fork disconnect; a later user-requested migration is a separate operation. These policies are particularly important for subscription cancellation and administrative organization deletion. + +No Project member table or grant backfill is required for the settled Issue-access model. Evaluate existing effective environment access and applicable permission-group policy at authorization time. Permission changes immediately affect access; do not materialize permanent Project grants. The Issue partial-access comparison uses non-archived environments, as confirmed by the user. Zero-active-environment Projects are permitted only in archived state, with their environment rows retained. Require at least one accessible active environment for ordinary active-Project access; use explicit archived-resource authorization for archive inspection/restore, never the empty all-environments predicate; do not treat an empty set as full access. External collaborators use their existing effective access, without introducing a new org-membership requirement solely for Issues. + + +Lifecycle behavior: existing Issues remain with the original Project after disconnect; explicit migration can be requested through Sim Chat/Mothership. Org admins or callers with admin on every environment may rename/archive. Archived environments are excluded from the Issue partial-access comparison. The separate all-environment administrative rule includes archived environments; the exclusion applies specifically to Issue access. + +Organization deletion — settled: preserve each Project and its Issues together and assign an explicit personal owner, matching the existing workspace-preservation pattern. Do this atomically with workspace detachment and organization deletion; do not rely on a bare SET NULL FK or infer authority from the billing payer. Detachment assigns the current organization owner as the Project lifecycle owner, matching workspace detachment. If the organization has no owner, the Project retains its required existing lifecycle owner. The FK prevents unresolved user ownership. Subscription lapse is non-destructive as well; it must never reuse a Project/Issue purge path. + +## Organization-deletion preservation contract + +Verified at this planning baseline: + +- `apps/sim/app/api/v1/admin/organizations/[id]/route.ts:302`: transaction calls detach, organization-resource cleanup enqueue, then org delete. Its deletion contract explicitly says workspaces survive; subscriptions referencing the org block deletion. +- `apps/sim/lib/workspaces/organization-workspaces.ts:426`: detach selects organization-mode workspaces without excluding archived rows, clears organization via payer transfer, sets mode to `grandfathered_shared`, and grants admin to the new billing account (org owner if present, otherwise workspace owner). This is not a workspace purge. +- `apps/sim/lib/organizations/resource-cleanup.ts`: captures directly organization-owned files/chats before FK cascade in a transactional outbox. It is not a blanket workspace-resource cascade. +- `apps/sim/lib/auth/auth.ts:1773`: Better Auth organization deletion is disabled. +- `apps/sim/lib/billing/webhooks/subscription.ts:140`: subscription dormancy calls workspace detach, retaining the org. Reusing this helper must not accidentally delete Projects/Issues when a subscription lapses. + +Enumerate affected Projects under the organization lifecycle lock before changing workspace organization/membership. Compare `project.organizationId` with canonical membership/workspace ownership, report inconsistent or mixed-tenant Projects, and include archived org Projects and report legacy empty-Project anomalies that a workspace-only join would miss. Resolve and validate an explicit personal owner for each Project, update ownership to personal, and detach its environments in the same transaction before the organization is deleted. Preserve Project IDs, membership rows, Issues and Issue attachments; do not enqueue them for org-resource cleanup. Existing direct org chat/file cleanup remains separate. Retire organization-specific permission-group bindings explicitly after scope transfer; personal access then uses current effective environment access under the agreed policy. Do not copy grants or restrictions from unrelated orgs. A transfer must not leave a surviving Project referencing a deleted group or organization. Record old/new ownership and audit the actual operator, not the new owner or payer. Restrictive ownership FKs force the lifecycle operation to complete explicitly; they are not a reason to purge data. + +## Column-by-column schema proposal + +The preferred schema below uses environment-derived access and supports the accepted personal Project model. All new identifiers use `generateId()` and text columns, matching workspace and organization IDs. IDs are immutable and independent of names, lineage roots and deployment environment. + +| Column | Proposal and reason | +| --- | --- | +| `project.id` | Text PK, application-generated UUID v4. Never reuse a workspace ID; never recompute after backfill. | +| `project.name` | Non-null text; trim, require 1–100 characters in domain validation and enforce compatible DB bounds/nonblank check. Allow duplicate names; IDs disambiguate. No slug or case-insensitive unique-name constraint without a product requirement. | +| `project.organizationId` | Nullable FK to organization, `ON DELETE RESTRICT`. Null only for explicitly personally owned Projects. An explicit lifecycle transaction transfers Projects before organization deletion. Organization transfer must include archived Projects and surface legacy empty-Project anomalies too. | +| `project.ownerId` (new) | Required user FK, `ON DELETE RESTRICT`, retained on both personal and organization Projects, matching the workspace lifecycle-owner model. Lifecycle ownership, not implicit access. Anchors lifecycle ownership independently of environment owners; individual removal of the last environment is prohibited. Personal-owner deletion transfers or explicitly deletes the Project before user deletion. | +| `project.archivedAt` | Nullable timestamp; setting it archives the Project and all member environments/workflows through one compound operation. Preserve archived environment rows and Issues. Archived detail remains readable to authorized members; normal lists omit it unless requested; Project/Issue mutations reject except restore or approved cleanup. | +| `project.createdAt` | Non-null timestamp default now, immutable. Backfill records actual creation time; retain existing environment history rather than pretending the Project existed earlier. | +| `project.updatedAt` | Non-null timestamp default now; explicitly set by Project metadata/ownership/archive writes. `defaultNow` alone is not an update trigger. Environment membership changes are audited and update the affected environment timestamp; policy changes update their policy records. | +| root/default environment, icon/color, pipeline position, billing fields | Omit from foundation. None is needed for Issue identity or authority. A default execution environment, if added, must still be independently authorized. | + +Indexes: organization list index on `(organization_id, archived_at, id)`; personal-owner list index on `(owner_id, archived_at, id)`; sort by stable ID cursor initially, avoiding a name-based cursor that changes on rename. Add a name-sort index only if the selected list contract requires it. Foreign-key referencing columns need indexes for ownership/deletion lookup. Do not duplicate a simple organization index without demonstrated need. + +| Environment membership field | Proposal | +| --- | --- | +| `project_workspace.projectId` | Non-null text FK to Project. Prefer `ON DELETE RESTRICT` while environments remain, so deleting a Project cannot silently orphan environments. Changing membership on disconnect is an explicit transaction. | +| `project_workspace.workspaceId` | Non-null workspace FK with cascade on workspace deletion; unique index enforces at most one Project per environment. Use a composite PK `(projectId, workspaceId)` plus unique workspace index; Project-leading PK supports member lookup. | +| `project_workspace.createdAt` | Non-null timestamp default now, recording membership-row creation. On reassignment retain creation time and audit the move; introduce assignment timestamp only if required by an actual consumer. | +| `workspace.forkedFromWorkspaceId` | Keep existing lineage edge and `ON DELETE SET NULL`. Explicit disconnect creates a new Project; implicit edge removal on hard parent deletion does not silently move Issue ownership. | +| `workspace.organizationId` | Retain for compatibility/resource authorization. Proposed same-organization invariant with Project uses null-safe comparison and shared transactional scope changes; deferred constraint triggers validate final state across Project, membership and workspace writes. | + +**Membership storage:** a direct non-null `workspace.projectId` would enforce exactly-one membership more simply, but the accepted design uses `project_workspace`; do not implement both. The unique workspace index only enforces at most one. Add deferred constraint triggers on workspace insert and membership deletion/update to require one membership for every surviving workspace at commit after rollout. Lock/recheck the workspace when reassignment can race; deletion must allow cascaded removal for a workspace that no longer exists. Ship this enforcement only after compatible writers and backfill are complete. Require at least one environment membership for each Project and at least one active environment for each active Project. Archived Projects retain member rows but may have no active environments. Complement application checks with deferred final-state constraints after rollout; source and destination Projects must be checked on membership moves. + +Do not add `project_member` for navigation or Issues. Extend the existing permission-group model with a Project-targeted restriction on Issue access for partial-access teammates. Its absence means no extra restriction. Resolve the Project organization canonically; only its org administrators may configure it. Reuse existing group assignment and policy-composition rules instead of inventing a per-user grant table. The current resolver in `lib/permission-groups/config-scope.server.ts` is workspace-keyed; add intentional Project scope rather than passing a selected/root environment. Exact storage follows the permission-group skill during implementation. Project rename/archive authority is org admin OR admin on every environment; do not infer admin from an arbitrary environment. Never grant administration through an empty all-environments check. Active empty Projects are invalid; archived-resource operations authorize explicitly against retained environment grants and org administration. + +## Membership and lifecycle invariants + +1. Every environment, including archived ones, belongs to exactly one Project after enforcement. Each Project contains at least one environment; active Projects have at least one active environment. Whole-Project archive permits zero active environments while retaining archived member rows. +2. Environment and Project organization scopes agree. Personal environments within a Project may have different lifecycle owners only if one explicitly selected Project owner governs Project lifecycle; that must not transfer environment ownership or grant access. +3. Fork creation inherits its parent's Project in the same transaction after rechecking current source scope under lock. Existing fork families seed Projects initially. Explicit disconnection splits the subtree into a new Project. Multiple roots can still arise through parent deletion; do not infer Issue movement from root counts alone. +4. Nested forks inherit the same Project; siblings need not have a unique depth. A disconnected fork and every descendant move together to a fresh Project. Never manufacture a new Project from a missing-parent/backfill race. Reevaluate Issue eligibility on both Projects after the split: a previously partial-access user may become full-access. Project-targeted permission-group restrictions also need explicit migration semantics; proposed preserve applicable restrictions on the new Project rather than accidentally resetting configured policy, while acknowledging the all-or-nothing rule now applies to each new environment set. Record policy handling and both Project IDs in the operation audit. Existing Issues stay with the original Project. Explicit user-requested migration through Sim Chat/Mothership must authorize source and destination Projects under one semantic application operation, with atomic/retry-safe movement and audit; it is an Issues feature, not a Project-foundation dependency. +5. Renaming an environment does not rename the Project; rename the Project through its own operation. Backfill and new Project creation use ` - Project`, as specified by the naming contract; workspace names remain untouched. Bound the generated Project name without losing its suffix, with `Untitled` for an empty source; surface any length normalization in the report. Duplicates are allowed and recorded, not merged. +6. Individual environment delete/archive must leave at least one active environment in an active Project and at least one member environment overall. Block removal of the last environment; direct hard-delete, account deletion, moves and fork disconnect cannot bypass the invariant. Serialize on the Project so two concurrent removals cannot each see the other as the remaining environment. For disconnect/move, validate both resulting Projects; refuse a split that strands the original as active with no active environment. Coordinated whole-Project lifecycle operations are the explicit exception for active-count checks, not an adapter flag that callers can freely set. +7. Project archive is one authorized compound operation that archives every environment and workflow, including operational deactivation already owned by existing archive paths. Retain membership rows and Issues. Reject new environment/fork/workflow admission into an archived Project. Preserve pre-existing archived states. Current `lib/workspaces/lifecycle.ts` commits workspace/resource archival before calling `archiveWorkflowsForWorkspace`; looping this helper across environments would permit a partially archived Project. Refactor transaction-owning primitives so durable Project/environment/workflow state changes commit atomically, with existing required external effects queued/retried after commit. Failure must not be reported as complete while workflows remain active. Project restore is deferred until a complete inverse exists; do not ship a metadata-only unarchive that leaves no active environments or silently revives resources archived before the Project. Archival does not imply cancellation of already-running executions beyond existing archive semantics unless separately specified. +8. Moving one environment between organizations cannot retain its old Project membership. For initial shipment, refuse a partial Project transfer unless the operation explicitly names a destination/new Project and defines the Issue outcome; preserve original Project Issues. A whole-Project transfer requires authorization and disclosure for all environments, retained Project data, collaborators and Issues. +9. Organization cancellation/detach/deletion must process Projects and all member environments atomically with existing payer transfers. The legacy per-owner workspace detach cannot silently determine how to split Project-wide data. If a complete, authorized transfer cannot be determined, stop before committing rather than silently orphaning or broadening access. +10. Project hard delete is not required to unblock Issues. Restrict it while environments or Issues exist. `issue.projectId` should be NOT NULL with restrictive deletion until an explicit purge policy is implemented. Deleting an account must account for personally owned Projects and retained Issues, not merely surviving workspace grants. + +## Authorization and application surface + +Provide a shared canonical Project application context: `{ projectId, organizationId, ownerId, archivedAt }`, loaded by ID, with optional asserted organization/workspace scope compared to canonical data. Return not-found for hidden/mismatched scope, consistent with shared concealment policy. Loading must be inside the authorized application-use-case lifecycle; an HTTP/tool adapter may not query protected membership itself. + +Create a small shared Project authorization wrapper alongside the existing workspace/organization wrappers. It must reject unsupported Principal kinds before loading protected data; authorize current effective environment access and applicable Project-scoped permission-group policy; distinguish Issue use from Project administration; respect archive state and typed errors; project semantic audit from authoritative results; and keep external side effects after success. Mutations lock and recheck Project state/authority sufficiently to serialize with environment access revocation, environment-set changes, policy changes, organization transfer and archive. Persist a durable audit/outbox record inside the transaction where existing shared infrastructure supports it; do not report a committed mutation as absent because a later notification fails. + +Do not use `defineAuthorizedWorkspaceUseCase` with an arbitrary root environment. Staging has both `defineWorkspaceOperation` and `defineOrganizationOperation`; the latter currently accepts session/organization-delegated principals and cannot model personal Project scope alone. Extend the shared foundation intentionally, rather than hiding authorization in `projects.ts` or borrowing a payer identity. + +Proposed first contract (session surface; supported principal expansion is explicit): + +| Operation | Authority and behavior | +| --- | --- | +| `projects.list` | Paginated Projects containing an accessible environment, in requested owner scope; archived Projects require a separate explicitly authorized archive listing, never vacuous environment-access checks; archived filter. No inaccessible environment IDs, names, counts or fork parent references. | +| `projects.get` | Basic navigation metadata visible through an accessible environment (archived-resource access uses a separate explicit policy); Issue and administrative capabilities checked separately; stable `{ id, name, organizationId, archivedAt, createdAt, updatedAt }` plus caller capabilities. | +| `projects.resolveForWorkspace` | Authorize environment access; it permits resolving basic Project navigation metadata through the canonical `project_workspace` membership, without requiring a separate Project grant. Never accept a supplied Project ID as truth. | +| `projects.listEnvironments` | Project navigation visibility plus each environment's existing access policy; filter hidden parents in lineage output. Paginate rather than embedding all environments in every Project result. | +| `projects.create`, `rename`, `archive` | Creation atomically creates the first environment under existing creation/quota policy. Rename/archive require org admin or admin on every environment; archive cascades to all member environments/workflows. Do not automatically add member grants. No zero-environment creation endpoint. Restore is deferred until full environment/workflow lifecycle behavior is defined. | +| `projects.addEnvironment` | One compound use case authorizing Project administration and existing workspace-creation policy, then atomically creating environment with correct Project membership. Legacy workspace creation still creates its own Project. | +| Project Issue restriction configuration | Existing permission-group application operations extended with Project scope; org-admin authorization and canonical group/Project organization matching. No separate Project membership CRUD. | +| Issue operations | Use the all-or-nothing Issue gate below; action-specific mutation policy remains to be agreed without introducing partial Issue visibility; canonical Issue lookup verifies `issue.projectId`. No environment ID required for Issue CRUD. Links to environment resources are individually authorized on resolution. | + +Issue authorization for a human caller uses the complete canonical environment set E and the subset A for which the caller currently has effective access. The server must compute E independently of the filtered environment-toggle response: + +```text +canViewProject = |A| > 0 +hasPartialEnvironmentAccess = |A| > 0 AND |A| < |E| +canUseProjectIssues = canViewProject + AND NOT (hasPartialEnvironmentAccess AND effectiveGroupPolicyDisablesIssuesForThisProject) +``` + +The agreed truth table is: no accessible environment -> no default Issue access; some environments -> all Issues by default, none when the restriction applies; all environments -> the partial-access restriction does not deny Issues. Normal authentication, principal-kind and archive/action rules still apply. Return Issue capability separately from navigation capability. Never return hidden environment names/IDs/counts merely to explain a denial. + +Enforce this once in the shared Project Issue authorization path used by list/detail/mutations, search, counts, notifications/subscriptions, exports and tools. A denied caller must not receive Issue titles or other Issue data through side channels. Existing environment resources linked from Issues still require their own environment checks; access to all Issues never grants production access. Ordinary Issue mutation roles belong to the Issues implementation, but may not create a per-environment Issue subset. Project rename/archive uses org-admin OR all-environment-admin authority; permission-group edits remain org-admin operations. + +Creation/deletion/archive/restoration/move of environments and access grants/revocations can change partial/full status. Reevaluate on the server and invalidate relevant capability/policy caches and live subscriptions. Serialize mutating checks with changes that can alter the decision. Do not select the policy using whichever environment the user toggled to. Missing policy defaults to allow, but a policy lookup failure must propagate as an error, not be interpreted as an absent restriction. Archived environments are excluded from this Issue access comparison, as confirmed by the user. + +Start Project HTTP routes at `/api/projects` and `/api/projects/[id]`, with named contracts under `lib/api/contracts/projects.ts` and shared builders. Add environment lookup and permission-group configuration routes as needed by the first consumer. Query hooks use `requestJson`, named contract types and React Query. Start with a session-only Project API; do not advertise unsupported key/delegation support. + +Workspace API keys and workspace-scoped executor/system principals **must not** gain project-wide Issue access just because their environment points at the Project. Personal API keys/OAuth need an explicit Project credential policy (there is no unambiguous environment `allowPersonalApiKeys` switch at this scope), scope enforcement and permission-group checks before v2 Project routes ship. Organization delegation already exists, but is documented/search-scoped; extend trusted audience/operation policies and recheck current subject membership before admitting Project operations. Org chat Project selection is a requested target only. No chat-to-Project relation, implicit default environment, or copied grant. + +The Issues implementation can build on the foundation contract. Existing environments become usable for Issues only after validated backfill/enforcement; new properly provisioned Projects can be exercised earlier behind a release gate. No promise of a production-ready ID resolver that invents IDs on reads. + +## Required code-path inventory + +Paths below are relative to the repository root. `Migrate` means membership/authorization/lifecycle behavior is in scope; it does not mean copying prototype implementations wholesale. + +| Path / ingress | Classification and required work | +| --- | --- | +| `packages/db/schema.ts`, `migrations/`, `migrations/meta/_journal.json` | Migrate: additive Project/environment-membership schema and Project-scoped permission-group restriction, later constraints; preserve staging's dashboard migration. | +| `packages/db/scripts/migrate.ts`, `script-migrations/index.ts`, `script-migrations/types.ts` | Migrate integration: SQL runs before registered scripts under a session migration lock. This lock does not stop application writers. Do not put a large unbounded backfill in startup. | +| `apps/sim/lib/workspaces/create.ts` | Migrate both `createWorkspaceInTransaction` and wrapper; Project, membership and workspace created atomically. Its `createDefaultPersonalWorkspaceInTransaction` covers enterprise owner-claim provisioning. | +| `apps/sim/app/api/workspaces/route.ts` | Migrate normal POST and lazy/default workspace creation through a shared authorized application creation operation; preserve legacy wire behavior and existing workspace URLs. | +| `apps/sim/lib/workspaces/application/create-organization-workspace.ts`; `lib/mothership/application/execute-organization-workspace-use-case.ts`; `lib/mothership/tools/server/workspaces.ts` | Migrate shared lower creation path; keep Mothership a trusted adapter and preserve org pinning, capabilities, billing admission and resource refresh behavior. | +| `apps/sim/lib/billing/enterprise-owner-claim.ts` | Migrate inherited transaction behavior; no nested independently committing Project creation. | +| `apps/sim/ee/workspace-forking/lib/create-fork.ts`; `application/`; `lib/lineage/{lineage,lineage-root,unlink}.ts` | Migrate Project inheritance/validation under existing rank-ordered lineage and row locks. Disconnect atomically creates a new Project and reassigns the complete descendant subtree. Repeated/concurrent unlink must not create duplicate Projects; preserve existing edge-lock ordering and reread the closure. Do not add an organization lock held across the entire copy without reconciling lock order. | +| `apps/sim/app/api/workspaces/[id]/route.ts`; `lib/workspaces/lifecycle.ts` | Migrate required use-case boundaries; environment rename remains independent, DELETE is archive; guard last-active-environment removal. Add Project-wide atomic archive primitives while preserving existing resource archival/deactivation semantics and retryable effects. | +| `apps/sim/lib/workflows/lifecycle.ts` | Account-related bulk workspace archive must obey Project last-environment invariants; full Project archival must enlist workflow state in the same compound operation. Transfer surviving Projects on account deletion or use an explicit whole-Project lifecycle path. Workflow restore is not workspace restore. | +| `apps/sim/lib/workspaces/organization-workspaces.ts` | Migrate attach/detach closure, locks, Project scope and personal-owner transfer together with existing payer changes; include archived environments/Projects and report legacy empty-Project anomalies. | +| `apps/sim/lib/invitations/core.ts`; `lib/billing/organization.ts`; `app/api/v1/admin/organizations/[id]/members/route.ts`; `scripts/consolidate-users-into-organization.ts` | Required attach callers. Reuse corrected transaction primitive; recompute Project visibility/Issue eligibility after environment grants or scope changes. Do not broadly refactor v1 APIs. | +| `apps/sim/lib/billing/webhooks/subscription.ts`; `app/api/v1/admin/organizations/[id]/route.ts` | Required detach/cancellation/delete callers; explicit Project lifecycle before restrictive org FK. Preserve atomic detach+delete and billing semantics. | +| `apps/sim/lib/workspaces/admin-move.ts`; `admin-move-source-impact.ts`; `lib/admin/member-operation.ts`; `lib/billing/enterprise-provisioning.ts`; `app/api/v1/admin/dashboard/workspaces/[id]/move/route.ts` | Migrate transfer preflight/disclosure, Project conflicts and transaction ownership. Existing moves can dissolve fork edges; that alone cannot define Project transfer. Keep admin operation receipts and recoverable audit. | +| `apps/sim/lib/billing/organizations/membership.ts` | Migrate member removal, external removal, member transfer and organization-owner transfer to invalidate Project Issue eligibility and handle personal lifecycle ownership alongside workspace permission rows. | +| `apps/sim/lib/users/account-deletion.ts` | Migrate deletion planning/rechecks/transaction for retained Issue data, owned Projects and last-environment guards, transfers and Project FK restrictions. Direct workspace deletion uses alias `workspaceTable`; a search only for `delete(workspace)` misses it. | +| `apps/sim/lib/workspaces/{list,public-queries,host-context,seed-workspace-list}.ts`; `application/list-*.ts`; `packages/platform-authz/` | Preserve existing environment authority. New Project reads must not replace resource scope or turn Project membership into environment permission. Add owner-context resolution only where needed. | +| `apps/sim/lib/workspaces/application/{manage-permissions,remove-member}.ts`; `permissions/`; `access/workspace-access.ts`; invitations and organization member operations | Required access inventory. Reevaluate any/all environment access at invite/removal and update live Issue access; do not materialize permanent Project grants. | +| `apps/sim/app/api/v2/workspaces/**`; `lib/api/contracts/v2/`; `lib/api/mcp/generated/v2-operations.ts`; SDK/CLI generation | Preserve existing environment API. Project APIs are an additive surface after credential policy, using the same Project application operations. Regenerate descriptors only when adding that surface. | +| `apps/sim/app/api/v1/admin/workspaces/[id]/{import,export}/route.ts`; `lib/workflows/operations/import-export.ts`; `lib/workspaces/__integration__/mapped-import.integration.ts` | Reviewed scope: import works into an existing environment, not a new Project. Preserve identity and never import Project grants/IDs from resource payloads. No broad v1 migration. | +| `apps/sim/background/cleanup-soft-deletes.ts`; `lib/billing/storage/{payer-transfer,tracking}.ts`; `lib/mothership/inbox/{lifecycle,settings-store}.ts` | Reviewed adjacent writers: resource cleanup, payer/accounting and inbox settings are not Project identity changes. Project archive must invoke established environment/workflow deactivation; preserve existing retention policy and do not add immediate hard purge. No blanket edits to every workspace update. | +| `apps/sim/lib/permission-groups/{config-scope.server,resolve.server,fields,mutation}.ts`; `application/`; group contracts/settings UI | Extend Project-scoped partial-access Issue restriction through existing authorized group operations. Default absent setting to allow; preserve other fields on update; resolve all applicable groups using existing composition rules. Validate group/Project organization and invalidate Project Issue capabilities on edits. Read `add-permission-group-item` before implementation. | +| New `apps/sim/lib/projects/application/**`, contracts, internal routes, hooks | Implement fresh against staging shared foundations. Session operations first; add other adapters only with intentional Principal policy. | +| Prototype `lib/mothership/chat/lifecycle.ts`, `chat/application/fork.ts`, `chat/list-mothership-chats.ts` and `copilot_chat_project` | Non-goal: exclude chat relation imports, writes, filters, backfill and schema. Separate chat-ownership work removes obsolete prototype relations if ever deployed there. | +| Prototype `app/o/[organizationId]/p/hooks/use-projects.ts` and resource-browser/UI port | Defer: consume true Project IDs only after foundation. Extract final behavior later; no prototype cherry-picks. | + +Implementation must repeat the write-ingress search after rebasing (including schema aliases/raw SQL, user cascades, scripts and fixture writers). This is a source-based inventory, not a claim that production rows already satisfy the invariants. + +## Backfill and deployment procedure + +### Expand and capture a plan + +1. Ship additive Project and membership tables without enforcing membership completeness yet; keep current UI/API functional. Do not rewrite workspace names, routes, resource IDs or chat rows. Ship compatible writers in the same foundation release, but account for old replicas during rollout. Backfill activation and final constraints are separate deployment steps. +2. Use `packages/db/scripts/backfill-projects.ts`, with explicit `PROJECT_BACKFILL_DATABASE_URL`, `PROJECT_BACKFILL_REPORT_PATH`, and `dry-run` / `apply --from-file --database-id --writers-drained`. Run with `bun --no-env-file` so a checkout’s `.env` cannot select the database. The operator must verify the supplied target; there is no implicit application-DSN fallback. +3. Scan all workspaces, including archived, in stable ID keyset pages. Discover graph components with cycle detection and bounded traversal; roots are rows with no parent, missing parents are reported, cycles and organization mismatches are blocking anomalies. Keep only one bounded family in memory. Committed Project and membership rows serve as the durable resume mapping. Never silently reconnect already detached lineages or group unrelated roots by equal name. +4. Group existing environments by their current fork connections. A root workspace and all forks still connected beneath it become one Project. For example, Production → Staging → Dev becomes one Project containing all three environments. If Staging was previously disconnected from Production, create two Projects: one for Production, and one for Staging plus Dev. A workspace with no connected forks gets its own Project. Each new Project uses the root workspace’s name plus “ - Project” and its `ownerId`. + + Preserve any valid Project assignment that already exists. If only some environments in the tree have been assigned to one compatible Project, add the remaining environments to that same Project. If the tree is assigned to multiple Projects or spans multiple organizations, report the conflict and leave it unchanged for review; do not automatically merge it. + + If all environments in the tree are archived, create an archived Project and use the most recent environment archive timestamp. Otherwise, create an active Project. Never reactivate an environment during migration. An existing Project with no environments is reported for repair because every Project must contain at least one environment. + +5. Allocate each proposed Project ID in the completed dry-run artifact with `generateId()`. Apply consumes that artifact and validates its database identity and family fingerprints. Reruns use the same planned IDs, including after an interrupted apply; compatible existing assignments are reused. No extra manifest table is required. +6. Duplicate names are allowed; bound and trim seed names deterministically and record before/after. An ID collision fails the batch; never silently replace a planned ID during apply. Never overwrite an unrelated Project on collision. No Project-member/grant backfill; existing groups gain an absent/default-allow restriction without changing existing settings. No chat backfill, no Issue backfill in this PR. + +### Coordinate writers and commit batches + +7. Before apply, drain old app/worker/script writers or use a bounded write-maintenance window for the affected lifecycle operations. The migration runner lock serializes migration runners only. A one-shot script registered in the startup runner may finish before old replicas stop creating unassigned environments and is not a readiness signal. +8. New create/fork/attach/detach/move writers and the backfill must share a documented lock order. Integrate with existing organization, invitation, payer, lineage and workspace locks; do not introduce an opposite Project→organization order. Lock scope owners/Project/family according to that rank plan, then member rows in stable code-unit ID order, re-read closure and fingerprints under lock, and retry if scope changed. For fork versus bulk attach/detach, the closure must be re-read after serialization so a newly committed child cannot be omitted. If online closure coordination cannot be proved, use the maintenance window for those operations. +9. Each normal family commits Project + all environment assignments atomically. Insert membership only for unassigned workspaces; a unique-workspace conflict requires a locked reread and validation of the expected Project, not silent success. Do not ignore unique conflicts. Keep counters/report progress after commit; counters must describe committed work. +10. Bound batches by rows and transaction duration, not only 200 roots. Oversized families are explicitly reported and processed in a dedicated controlled transaction/window, or through a staged assignment design whose incomplete state is hidden from Project consumers. Never expose a partially migrated Project as complete. Resume from the original dry-run plan and committed memberships; recover committed batches after process death without new IDs or accidental policy restrictions. +11. Keep legacy workspace UI operational while Project/Issues reads are release-gated until readiness. During rolling deployment a missing membership is an explicit not-ready state, not permission to create a new Project on a GET. A compatible fork writer must ensure its parent family transactionally, or temporarily refuse fork creation for an unassigned family. + +### Validate and enforce + +The command must emit counts and offending IDs for at least these checks (illustrative SQL for the proposed schema): + +```sql +-- Must be zero before exactly-one enforcement. +SELECT w.id FROM workspace w +LEFT JOIN project_workspace pw ON pw.workspace_id = w.id +WHERE pw.workspace_id IS NULL; + +-- Must be zero; use null-safe comparison for personal scope. +SELECT w.id, pw.project_id FROM workspace w +JOIN project_workspace pw ON pw.workspace_id = w.id +JOIN project p ON p.id = pw.project_id +WHERE w.organization_id IS DISTINCT FROM p.organization_id; + +-- Existing fork edges must stay in the same Project. +SELECT child.id FROM workspace child +JOIN project_workspace child_pw ON child_pw.workspace_id = child.id +JOIN project_workspace parent_pw ON parent_pw.workspace_id = child.forked_from_workspace_id +WHERE child_pw.project_id IS DISTINCT FROM parent_pw.project_id; + +-- No active Project may lack active environments. +SELECT p.id FROM project p +WHERE p.archived_at IS NULL AND NOT EXISTS ( + SELECT 1 FROM project_workspace pw JOIN workspace w ON w.id = pw.workspace_id + WHERE pw.project_id = p.id AND w.archived_at IS NULL +); + +-- Even archived Projects retain at least one environment row. +SELECT p.id FROM project p WHERE NOT EXISTS ( + SELECT 1 FROM project_workspace pw WHERE pw.project_id = p.id +); + +-- Every Project retains a user lifecycle owner. +SELECT id FROM project +WHERE owner_id IS NULL; + +``` + +Additionally validate FK orphans, cycles/missing parents, duplicate/partial manifest assignment, permission-group Project/organization mismatches, counts of archived environments, empty Projects, source permission changes since planning, and no Project membership implied by chat data. Empty Projects and active Projects with no active environments are blocking anomalies; repair them explicitly, never silently delete retained Issues. Duplicate names and fully archived Projects with retained environments are valid. Check plan/report counts against actual committed rows and ensure every blocked family is resolved before completion. + +12. After compatible writers are fully deployed and validation passes, add/validate constraints, enforce exactly-one membership, and enable consumers. Use staged `NOT VALID` validation for existing-row constraints where appropriate; lock-time-bounded DDL and staging's documented concurrent-index mechanism for large workspace indexes. A cross-table deferred trigger must validate both workspace and Project ownership updates and serialize conflicting writes; test concurrent transactions, not just final-state SQL. Ordinary FKs still own referential existence. +13. Repeat validation after a normal write interval. Confirm a fresh install and an upgraded self-hosted database reach the same ready state. An operator-run production backfill needs a documented self-hosted upgrade path; readiness must remain false until it completes. Do not silently hang all deploys on a production-sized graph scan. + +### Recovery and rollback + +Before enforcement/consumer activation, application rollback may ignore additive schema, but old writers require pausing the backfill and another reconciliation before reactivation. After exactly-one membership enforcement, rolling back to old writers is unsafe: they cannot create environments. Roll back only to the compatible foundation release, or deliberately relax the constraint with writes gated and rerun reconciliation afterward. + +Keep IDs, membership rows, and operator reports durable. If a backfilled grouping is wrong before Issues begins, correct it transactionally from the recorded mapping with an explicit reviewed repair. Once Issues reference a Project, do not undo by dropping Projects or regenerating IDs; use a forward repair with an explicit Issue retention/move decision. Preserve backups and reports. No automatic destructive down migration. Retry failed batches; committed family assignments survive. A script runner's completed-name marker is not a substitute for a row-level readiness check. + +## PR and dependency sequence + +| PR | Scope and dependency | Deploy boundary | +| --- | --- | --- | +| A — Project foundation | Clean implementation branch from fresh staging; schema expansion, environment-derived Project/Issue access and Project-scoped partial-access restriction, stable IDs and session contracts, shared creation/fork writes, required lifecycle compatibility, dry-run/apply tooling and focused proofs. No chat/UI. | Additive deployment; existing workspace UI works; Project consumer gate initially off. Project/environment placement are atomic. Required org/user lifecycle handling must not be deferred past activation. | +| B — migration enforcement | Depends on A: bounded apply runbook, validated data/readiness, constraints and activation. Can be a small separate PR/release, or a second deployment phase of A. | Must follow compatible-writer rollout and measured validation. No hardcoded success flag standing in for data integrity. | +| C — Issues | Issues stacks on A after access/lifecycle contract is agreed; merges against A and activates after B. Issue FK, application operations, routes and any feature UI independently reviewable; enforce the partial-access restriction across every Issue surface before enabling it. | Issue identity is Project ID; no environment-owner authorization fallback. | +| D — chat ownership | Independent of A/B/C. Org/personal chat ownership and resource browsing, with obsolete prototype Project references excluded. | No chat-to-Project relation or Project migration dependency. | +| E — remaining backend features | Resource-browser APIs, environment management and other feature foundations as separate PRs. Dashboards already exist in staging; do not reintroduce prototype migration. | Depends on A/B only where actual Project operations are used. | +| F — new organization/Project UI | Built on landed feature surfaces; extract completed behavior from the aggregate experiment. True Project ID and selected environment ID remain distinct. | Last; current workspace navigation remains supported until replacement is deliberately shipped. | + +If A becomes too large, split a prerequisite shared authorization/closure-lock fix from the entity PR, then stack A on it. Do not split out required lifecycle writes such that an enabled Project entity can silently corrupt on cancellation, invitation acceptance or account deletion. A schema-only merge can be additive, but is not the complete foundation required by Issues. + + +## Verification plan and release evidence + +Apply the `test-audit` authoring gate before code: enumerate failure modes, choose one strongest owner boundary, and avoid schema/registry/config assertions. Use disposable Postgres/Redis and an isolated app. Report to caller-supplied `PROJECT_FOUNDATION_REPORT_PATH` / `PROJECT_BACKFILL_REPORT_PATH` with each check's status, duration, failure and IDs; upload on CI failure. + +| Real boundary | Failure to prove | +| --- | --- | +| Migration against pre-Project DB snapshot | Active/archived singleton, siblings, nested forks, detached family, duplicate names, mixed owners, missing parent, cycle, cross-org family, prior partial/split assignments. Valid rows map once; anomalies block without mutation; rerun uses identical IDs and preserves default-allow policy. | +| Real concurrent DB transactions | Fork versus backfill; fork versus attach/detach/move; whole-Project archive versus fork/environment/workflow creation; two concurrent last-environment removals; archive versus Issue write; environment access revoke versus Issue write; full-to-partial transition versus Issue write; restriction edits versus cached reads; crash between batches. Assert committed scope and recoverability, not mocked call order. | +| Real create/provision/fork HTTP or application boundary | Workspace+Project rollback together after injected transactional failure; regular creation, first visit, enterprise claim and org Mothership creation all obey membership. | +| Real Project/Issue HTTP | Staging-only grant shows the Project and only staging in its toggle, with production hidden; partial-access user sees all Issues by default and none with the applicable group restriction; full-access user retains Issues under that restriction; no-environment user gains nothing; restriction changes do not hide accessible environments; list/detail/search/counts/notifications/tools honor denial; cross-org assertion concealed; workspace API key rejected; archived Project read allowed/write denied; active empty Project creation/removal is rejected; archived-state checks cannot grant access through an empty set; revocation effective without stale authorization cache. | +| Lifecycle E2E | Independent renames; disconnect creates one new Project for the complete subtree, retains original Project ID, and leaves existing Issues in the original Project; individual archive/delete of last environment is rejected; whole-Project archive covers every environment/workflow atomically and retains Issues; org cancellation/delete produces approved personal ownership or atomic refusal; user deletion handles retained Issues and last-environment invariants; transfer rollback preserves billing and scope. | +| Current UI compatibility | Existing workspace URLs, resource execution and import/export still work; no chat migration; Project selection cannot choose an execution environment implicitly. | +| Upgrade/install | Fresh install, interrupted backfill resume, rolling old/new writers before enforcement, enforced-release rollback boundary and self-hosted completion path. | + +Extend existing real suites where they own the failure: `lib/workspaces/__integration__/fork-sync.integration.ts`, `http-cli.integration.ts`, `mapped-import.integration.ts`, and `ee/workspace-forking/lib/lineage/fork-lock-order.integration.ts`; read their full coverage before deciding on additions. Project authorization and migration have new independent contracts and need real owner-boundary proofs. Existing type checks cannot prove concurrent ownership integrity. Demonstrate regressions go red with each relevant guard removed; never add a test that only restates a table definition. + +Implementation checks: relevant integration/E2E runs; workspace type-checks (app/db/auth/platform-authz as touched); `bun run check:api-validation:strict`; `bun run check:audits`; repository lint with review of autofixes; `git diff --check`. Local validation now includes 35 foundation/fork/lifecycle integration tests, 9 dedicated backfill/CLI integration cases, app/database type checks, lint, all 57 audits and diff checks. A negative control confirmed the changed-family guard catches a newly added descendant. These checks do not represent full coverage of every future-consumer scenario in the matrix above. No hosted backfill, browser rehearsal or production activation has occurred. + + +### Ownership clarification during implementation + +Every Project retains a required `ownerId`, including organization Projects. `organizationId` is optional; the two fields are not mutually exclusive. This matches workspace lifecycle ownership. Neither field substitutes for explicit workspace grants or organization-admin authority. A departing owner is transferred before account deletion; Project ownership does not cascade away with a user. Organization detachment keeps Project identity and explicitly assigns its personal lifecycle owner. + + +## Concrete implementation and rollout notes + +The schema contains `project` (`id`, `name`, required `ownerId`, nullable `organizationId`, `archivedAt`, `createdAt`, `updatedAt`) and `project_workspace` (`projectId`, unique `workspaceId`, `createdAt`). There is no stored fork depth, position, or Project billing state. + +Implemented session routes: + +- `GET /api/projects`: authorized, cursor-paginated active Project inventory. +- `GET /api/projects/[id]`: metadata, accessible active environments, and administration/Issues capabilities. +- `GET /api/projects/by-workspace/[workspaceId]`: canonical lookup that also authorizes the requested environment. +- `PATCH /api/projects/[id]`: rename. +- `DELETE /api/projects/[id]`: compound Project/environment/workflow archive. + +Workspace creation creates its Project atomically; fork creation joins the existing Project. These are the creation surfaces in this foundation. A separate empty-Project create, arbitrary environment attachment, restore UI, public-key API, and Issues CRUD are not implemented here. The existing permission-group editor gains the Project Issues restriction; the new Project navigation UI remains a separate consumer. + +`deniedPartialAccessProjectIssues` is a Project-ID denylist on existing permission-group config. An empty list allows access. For a partial-access teammate, each accessible active environment resolves its effective group using existing explicit-member/all-members/default precedence. If any of those effective groups denies this Project, all Issues are denied. The selected environment cannot change the answer. Archived environments are excluded from this comparison. Full-access teammates bypass this partial-access restriction. Issue use cases call `authorizeProject` in their own transaction before reading or writing Issue rows; `getProjectIssueAccess` is only a read-only probe. The Issue gate holds the permission-group lock through that transaction, so callers must respect its existing leaf-lock rule. + +Disconnect copies a matching restriction to the new Project. Moving a Project out of an organization removes its stale ID from that organization’s group configs. Rename/archive authorization includes every environment, including archived members. A departing organization member’s Project lifecycle ownership moves to the organization owner. Account teardown transfers surviving Projects to an administrator of their surviving environments and explicitly removes wholly private Projects in the same teardown transaction; it cannot leave a surviving active Project with no active environment. + +The backfill scans 100 workspaces per page, limits ancestor traversal to 1,000 links and CLI family batches to 1,000 environments by default (10,000 maximum), and commits one family at a time. It reports cycles, missing parents, oversized families, cross-organization lineages, conflicting assignments and inconsistent archive states. A reserved session advisory lock excludes overlapping backfill runs. Apply takes bounded NOWAIT table locks before rediscovering and validating the full family. Organization moves use a nonblocking Project lock because their legacy paths can already hold workspace locks; contention returns a retryable conflict. A conflicting batch leaves prior committed families intact and is safe to rerun after repair. + +Operator sequence: + +1. Deploy the additive migration and compatible writers. Do not enable downstream Project/Issues consumers yet. +2. Run dry-run with an explicitly verified database target and inspect its report. +3. Drain old writers and pause lineage/ownership maintenance while applying. Pass `apply --from-file --database-id --writers-drained`; this flag is an operator assertion, not an automatic drain. Legacy unassigned workspaces remain usable, but their fork operation returns a backfill-required conflict until assignment completes. +4. Rerun and verify zero new assignments and zero conflicts, then execute the reconciliation queries in this plan. Resolve any remaining unassigned/empty/mismatched rows before activation. +5. Enable consumers only after reconciliation. Complete database-level membership/minimum-environment enforcement in a later deployment, as recorded by `contract-pending` in the schema. Do not activate consumers on old-writer rollback without another reconciliation. + +Example invocation from the repository root (supply the target URL through your normal secret mechanism): + +```sh +PROJECT_BACKFILL_REPORT_PATH=/absolute/path/project-backfill-dry-run.json \ +bun --no-env-file packages/db/scripts/backfill-projects.ts dry-run + +PROJECT_BACKFILL_REPORT_PATH=/absolute/path/project-backfill-apply.json \ +bun --no-env-file packages/db/scripts/backfill-projects.ts apply \ + --from-file /absolute/path/project-backfill-dry-run.json \ + --database-id --writers-drained +``` + +Validation artifacts come from `PROJECT_FOUNDATION_REPORT_PATH` (or `apps/sim/test-results/project-foundation.json`) and the integration runner’s JSON report. CI already uploads `apps/sim/test-results/*.json`. The former mock-count workspace lifecycle tests were replaced by real database checks of concurrent removal and retry repair. The dedicated backfill suite executes the exact additive migration in a disposable database before exercising backfill. External webhook/socket/MCP cleanup retains existing best-effort post-commit behavior; durable archive state commits atomically. + +The operator-run CLI procedure, self-hosted commands, SQLSTATE handling, lock behavior, report interpretation and repair procedure are documented in [the Project backfill runbook](./project-backfill-runbook.md). diff --git a/knip.jsonc b/knip.jsonc index 4ab2873da7a..ea731f3397f 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -42,6 +42,8 @@ // Generated contracts mirror their source of truth; regenerating them must not // trip the unused-export ratchet, and hand edits would be overwritten. "lib/mothership/generated/**": ["exports", "types", "duplicates"], + // API conventions require exported named wire schemas and aliases, including before client adoption. + "lib/api/contracts/projects.ts": ["exports", "types"], "sandbox-tasks/index.ts": ["files"], "components/mcp/index.ts": ["files"], "triggers/quickbooks/index.ts": ["files"], diff --git a/packages/audit/src/types.ts b/packages/audit/src/types.ts index 7cd5acb84d4..6505bf2431a 100644 --- a/packages/audit/src/types.ts +++ b/packages/audit/src/types.ts @@ -218,6 +218,8 @@ export const AuditAction = { WORKFLOW_EXPORTED: 'workflow.exported', WORKSPACE_CREATED: 'workspace.created', + PROJECT_UPDATED: 'project.updated', + PROJECT_ARCHIVED: 'project.archived', WORKSPACE_UPDATED: 'workspace.updated', WORKSPACE_DELETED: 'workspace.deleted', WORKSPACE_DUPLICATED: 'workspace.duplicated', @@ -287,6 +289,7 @@ export const AuditResourceType = { USER: 'user', WEBHOOK: 'webhook', WORKFLOW: 'workflow', + PROJECT: 'project', WORKSPACE: 'workspace', } as const diff --git a/packages/db/migrations/0393_project_foundation.sql b/packages/db/migrations/0393_project_foundation.sql new file mode 100644 index 00000000000..5a50b354418 --- /dev/null +++ b/packages/db/migrations/0393_project_foundation.sql @@ -0,0 +1,25 @@ +CREATE TABLE "project" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "organization_id" text, + "owner_id" text NOT NULL, + "archived_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "project_name_length" CHECK (char_length(btrim("project"."name")) BETWEEN 1 AND 100) +); +--> statement-breakpoint +CREATE TABLE "project_workspace" ( + "project_id" text NOT NULL, + "workspace_id" text NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "project_workspace_project_id_workspace_id_pk" PRIMARY KEY("project_id","workspace_id") +); +--> statement-breakpoint +ALTER TABLE "project" ADD CONSTRAINT "project_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "project" ADD CONSTRAINT "project_owner_id_user_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "project_workspace" ADD CONSTRAINT "project_workspace_project_id_project_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."project"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "project_workspace" ADD CONSTRAINT "project_workspace_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "project_organization_archive_id_idx" ON "project" USING btree ("organization_id","archived_at","id");--> statement-breakpoint +CREATE INDEX "project_owner_archive_id_idx" ON "project" USING btree ("owner_id","archived_at","id");--> statement-breakpoint +CREATE UNIQUE INDEX "project_workspace_workspace_id_unique" ON "project_workspace" USING btree ("workspace_id"); \ No newline at end of file diff --git a/packages/db/migrations/meta/0393_snapshot.json b/packages/db/migrations/meta/0393_snapshot.json new file mode 100644 index 00000000000..9f358a08b88 --- /dev/null +++ b/packages/db/migrations/meta/0393_snapshot.json @@ -0,0 +1,29715 @@ +{ + "id": "ba7c9a7d-a315-42bc-9de6-931239aa626b", + "prevId": "b50de40a-fb8e-4b11-bd00-a0080b6ee7ef", + "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.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 + }, + "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": "'{}'" + }, + "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": {} + } + }, + "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" + } + }, + "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.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_search": { + "name": "embedding_keyword_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 + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "embedding_keyword_search_kb_idx": { + "name": "embedding_keyword_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_document_idx": { + "name": "embedding_keyword_search_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_content_idx": { + "name": "embedding_keyword_search_content_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_search_id_embedding_id_fk": { + "name": "embedding_keyword_search_id_embedding_id_fk", + "tableFrom": "embedding_keyword_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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_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.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_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" + ] + }, + "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": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json index 6560207fc2c..d97e885e7ee 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -2745,6 +2745,13 @@ "when": 1790723607543, "tag": "0392_dashboard", "breakpoints": true + }, + { + "idx": 393, + "version": "7", + "when": 1790912634834, + "tag": "0393_project_foundation", + "breakpoints": true } ] } diff --git a/packages/db/package.json b/packages/db/package.json index 31cf5145650..9ff079b5d8c 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -28,6 +28,10 @@ "./sso-primary-provider": { "types": "./sso-primary-provider.ts", "default": "./sso-primary-provider.ts" + }, + "./project-backfill": { + "types": "./project-backfill.ts", + "default": "./project-backfill.ts" } }, "scripts": { diff --git a/packages/db/project-backfill.ts b/packages/db/project-backfill.ts new file mode 100644 index 00000000000..181cfe17246 --- /dev/null +++ b/packages/db/project-backfill.ts @@ -0,0 +1,471 @@ +import { createHash } from 'node:crypto' +import { createLogger } from '@sim/logger' +import { classifyDatabaseFailure, getPostgresErrorCode } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import { backoffWithJitter } from '@sim/utils/retry' +import { compareStrings, truncate } from '@sim/utils/string' +import postgres, { type Sql, type TransactionSql } from 'postgres' + +type BackfillSql = Sql | TransactionSql + +const logger = createLogger('ProjectBackfill', { enabled: true, logLevel: 'INFO' }) +const MAX_FAMILY = 10_000 +const MAX_WORKSPACES = 250_000 +const RUN_LOCK = 'sim-project-backfill-v1' + +interface LegacyWorkspace { + id: string + name: string + organization_id: string | null + owner_id: string + archived_at: string | null + forked_from_workspace_id: string | null +} + +interface FamilyPlan { + rootId: string + projectId: string + workspaceIds: string[] + fingerprint: string + name: string +} + +interface Conflict { + workspaceId: string + projectIds: string[] + workspaceIds: string[] + reason: string +} + +export interface ProjectBackfillReport { + version: 1 + databaseId: string + mode: 'dry-run' | 'apply' | 'verify' + startedAt: string + finishedAt?: string + completed: boolean + ready: boolean + stopped?: string + errorCode?: string + scanned: number + families: number + createdProjects: number + assignedWorkspaces: number + unchangedFamilies: number + retries: number + plan: FamilyPlan[] + conflicts: Conflict[] +} + +interface BackfillOptions { + mode: ProjectBackfillReport['mode'] + databaseId: string + plan?: ProjectBackfillReport + maxFamilySize?: number + seconds?: number + shouldStop?: () => boolean + onProgress?: (report: ProjectBackfillReport) => Promise +} + +function fingerprint(family: LegacyWorkspace[]): string { + return createHash('sha256').update(JSON.stringify(family)).digest('hex') +} + +function conflict( + rootId: string, + reason: string, + ids: string[], + projects: string[] = [] +): Conflict { + return { workspaceId: rootId, reason, workspaceIds: ids, projectIds: projects } +} + +/** Only reads and transactions known to have rolled back may be retried. */ +async function withDatabaseRetry( + operation: () => Promise, + write: boolean, + report: ProjectBackfillReport, + stopped: () => boolean +): Promise { + for (let attempt = 1; ; attempt++) { + try { + return await operation() + } catch (error) { + const code = getPostgresErrorCode(error) + const category = classifyDatabaseFailure(error) + const retryable = write + ? ['40001', '40P01', '55P03'].includes(code ?? '') + : category === 'connection' || ['53300', '57P03'].includes(code ?? '') + if (!retryable || attempt >= 3 || stopped()) throw error + report.retries++ + logger.warn('Retrying Project backfill database operation', { code, write, attempt }) + await sleep(backoffWithJitter(attempt, null, { baseMs: 250, maxMs: 2000 })) + } + } +} + +async function configureTransaction(tx: BackfillSql) { + await tx`SET LOCAL lock_timeout = '2s'` + await tx`SET LOCAL statement_timeout = '30s'` + await tx`SET LOCAL idle_in_transaction_session_timeout = '30s'` + await tx`SET LOCAL transaction_timeout = '30s'` +} + +async function discoverFamily(tx: BackfillSql, rootId: string, maximum: number) { + const family = await tx` + WITH RECURSIVE family AS ( + SELECT id, name, organization_id, owner_id, archived_at::text AS archived_at, forked_from_workspace_id FROM workspace WHERE id = ${rootId} + UNION + SELECT w.id, w.name, w.organization_id, w.owner_id, w.archived_at::text, w.forked_from_workspace_id + FROM workspace w JOIN family f ON w.forked_from_workspace_id = f.id + ) SELECT * FROM family LIMIT ${maximum + 1} + ` + return family.sort((a, b) => compareStrings(a.id, b.id)) +} + +async function inspectFamily(tx: BackfillSql, rootId: string, maximum: number) { + const family = await discoverFamily(tx, rootId, maximum) + const root = family.find((row) => row.id === rootId) + const ids = family.map((row) => row.id) + if (!root || root.forked_from_workspace_id !== null) + return { conflict: conflict(rootId, 'Root missing or now attached to another family', ids) } + if (family.length > maximum) + return { conflict: conflict(rootId, 'Family exceeds batch limit', ids) } + if (family.some((row) => row.organization_id !== root.organization_id)) + return { conflict: conflict(rootId, 'Family spans organizations', ids) } + const memberships = await tx<{ project_id: string }[]>` + SELECT DISTINCT project_id FROM project_workspace WHERE workspace_id = ANY(${ids}::text[]) ORDER BY project_id + ` + const projectIds = memberships.map((row) => row.project_id) + if (projectIds.length > 1) + return { conflict: conflict(rootId, 'Family spans multiple Projects', ids, projectIds) } + const existingId = projectIds[0] + if (existingId) { + const [existing] = await tx<{ organization_id: string | null; archived_at: Date | null }[]>` + SELECT organization_id, archived_at FROM project WHERE id = ${existingId} + ` + if (!existing || existing.organization_id !== root.organization_id) + return { conflict: conflict(rootId, 'Project organization differs', ids, projectIds) } + const outside = + await tx`SELECT workspace_id FROM project_workspace WHERE project_id = ${existingId} AND NOT (workspace_id = ANY(${ids}::text[])) LIMIT 1` + if (outside.length) + return { conflict: conflict(rootId, 'Project includes another lineage', ids, projectIds) } + if (Boolean(existing.archived_at) !== family.every((row) => row.archived_at !== null)) + return { conflict: conflict(rootId, 'Project archive state differs', ids, projectIds) } + } + const [{ missing }] = await tx<{ missing: number }[]>` + SELECT count(*)::int AS missing FROM workspace w WHERE w.id = ANY(${ids}::text[]) + AND NOT EXISTS (SELECT 1 FROM project_workspace pw WHERE pw.workspace_id = w.id) + ` + return { root, family, ids, existingId, missing } +} + +async function applyFamily(tx: BackfillSql, planned: FamilyPlan, maximum: number) { + // NOWAIT prevents waiting behind application writers with an opposite row/advisory lock order. + await tx`LOCK TABLE workspace, project, project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT` + const state = await inspectFamily(tx, planned.rootId, maximum) + if (state.conflict) return { conflict: state.conflict, created: 0, assigned: 0 } + if (fingerprint(state.family) !== planned.fingerprint) + return { + conflict: conflict( + planned.rootId, + 'Family changed since dry-run; produce a new plan', + state.ids + ), + created: 0, + assigned: 0, + } + if (state.existingId && state.existingId !== planned.projectId) + return { + conflict: conflict(planned.rootId, 'Project assignment changed since dry-run', state.ids, [ + state.existingId, + planned.projectId, + ]), + created: 0, + assigned: 0, + } + if (!state.existingId) { + await tx`INSERT INTO project (id, name, organization_id, owner_id, archived_at) + SELECT ${planned.projectId}, ${planned.name}, ${state.root.organization_id}, ${state.root.owner_id}, + CASE WHEN count(archived_at) = count(*) THEN max(archived_at) ELSE NULL END + FROM workspace WHERE id = ANY(${state.ids}::text[])` + } + const inserted = await tx`INSERT INTO project_workspace (project_id, workspace_id) + SELECT ${planned.projectId}, id FROM workspace WHERE id = ANY(${state.ids}::text[]) + AND NOT EXISTS (SELECT 1 FROM project_workspace pw WHERE pw.workspace_id = workspace.id)` + return { created: state.existingId ? 0 : 1, assigned: inserted.count } +} + +async function inspectGlobalState(tx: BackfillSql) { + const rows = await tx<{ id: string; reason: string }[]>` + SELECT p.id, 'Empty Project' AS reason FROM project p + WHERE NOT EXISTS (SELECT 1 FROM project_workspace pw WHERE pw.project_id = p.id) + UNION ALL + SELECT pw.project_id, 'Orphan membership or organization mismatch' FROM project_workspace pw + LEFT JOIN project p ON p.id = pw.project_id LEFT JOIN workspace w ON w.id = pw.workspace_id + WHERE p.id IS NULL OR w.id IS NULL OR p.organization_id IS DISTINCT FROM w.organization_id + LIMIT 1001 + ` + if (rows.length > 1000) throw new Error('Global anomaly report limit exceeded; repair and rerun') + return rows.map((row) => conflict('', row.reason, [], [row.id])) +} + +/** Validate the operator artifact before it can select any write target. */ +export function readProjectBackfillPlan(value: unknown, databaseId: string): ProjectBackfillReport { + if (!value || typeof value !== 'object') throw new Error('Invalid dry-run artifact') + const plan = value as Partial + if ( + plan.version !== 1 || + plan.mode !== 'dry-run' || + !plan.completed || + plan.databaseId !== databaseId || + !Array.isArray(plan.plan) || + !Array.isArray(plan.conflicts) || + plan.conflicts.length + ) + throw new Error('Apply requires a completed, conflict-free dry-run for this database') + let total = 0 + const roots = new Set() + const projects = new Set() + const members = new Set() + for (const family of plan.plan) { + if ( + !family || + typeof family.rootId !== 'string' || + !family.rootId || + typeof family.projectId !== 'string' || + !family.projectId || + typeof family.fingerprint !== 'string' || + !/^[a-f0-9]{64}$/.test(family.fingerprint) || + typeof family.name !== 'string' || + family.name.trim().length < 1 || + family.name.length > 100 || + !Array.isArray(family.workspaceIds) || + !family.workspaceIds.length || + family.workspaceIds.length > MAX_FAMILY + ) + throw new Error('Malformed family in dry-run artifact') + if (roots.has(family.rootId) || projects.has(family.projectId)) + throw new Error('Duplicate planned root or Project') + roots.add(family.rootId) + projects.add(family.projectId) + for (const id of family.workspaceIds) { + if (typeof id !== 'string' || !id || members.has(id)) + throw new Error('Invalid or repeated planned environment') + members.add(id) + } + if (!family.workspaceIds.includes(family.rootId)) + throw new Error('Root missing from dry-run family') + total += family.workspaceIds.length + if (total > MAX_WORKSPACES) throw new Error('Dry-run artifact exceeds workspace limit') + } + return plan as ProjectBackfillReport +} + +/** Bounded operator backfill. Reports are checkpointed before writes and after each commit. */ +export async function backfillProjects( + sql: Sql, + options: BackfillOptions +): Promise { + const maximum = options.maxFamilySize ?? MAX_FAMILY + const seconds = options.seconds ?? 600 + if ( + !Number.isInteger(maximum) || + maximum < 1 || + maximum > MAX_FAMILY || + !Number.isInteger(seconds) || + seconds < 1 || + seconds > 3600 + ) + throw new Error('Invalid family or runtime limit') + const source = + options.mode === 'apply' ? readProjectBackfillPlan(options.plan, options.databaseId) : null + const report: ProjectBackfillReport = { + version: 1, + databaseId: options.databaseId, + mode: options.mode, + startedAt: new Date().toISOString(), + completed: false, + ready: false, + scanned: 0, + families: 0, + createdProjects: 0, + assignedWorkspaces: 0, + unchangedFamilies: 0, + retries: 0, + plan: [], + conflicts: [], + } + const deadline = Date.now() + seconds * 1000 + const stopped = () => Boolean(options.shouldStop?.()) || Date.now() >= deadline + const checkpoint = async () => { + await options.onProgress?.(report) + } + let lockLost = false + if (sql.options.host.length !== 1 || sql.options.port.length !== 1) + throw new Error('Backfill requires a single direct primary endpoint') + const lockClient = postgres({ + host: sql.options.host[0], + port: sql.options.port[0], + database: sql.options.database, + user: sql.options.user, + password: sql.options.pass ?? undefined, + ssl: sql.options.ssl, + connection: sql.options.connection, + onnotice: sql.options.onnotice, + max: 1, + idle_timeout: 0, + max_lifetime: null, + connect_timeout: 10, + onclose: () => { + lockLost = true + }, + }) + const connection = await lockClient.reserve() + let locked = false + let backendPid = 0 + try { + // Session settings also bound discovery and the first lock attempt. + await connection`SET statement_timeout = '30s'` + await connection`SET lock_timeout = '2s'` + const [identity] = await connection<{ pid: number; locked: boolean }[]>` + SELECT pg_backend_pid() AS pid, pg_try_advisory_lock(hashtextextended(${RUN_LOCK}, 0)) AS locked + ` + locked = identity.locked + backendPid = identity.pid + if (!locked) throw new Error('Another Project backfill run holds the maintenance lock') + const assertLock = async () => { + if (lockLost) throw new Error('Project backfill maintenance lock was lost') + const [state] = await connection<{ held: boolean }[]>` + SELECT pg_backend_pid() = ${backendPid} AND EXISTS ( + SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND pid = pg_backend_pid() + AND classid = ((hashtextextended(${RUN_LOCK}, 0) >> 32) & 4294967295)::oid + AND objid = (hashtextextended(${RUN_LOCK}, 0) & 4294967295)::oid AND objsubid = 1 AND granted + ) AS held + ` + if (!state.held) throw new Error('Project backfill maintenance lock was lost') + } + const transaction = async ( + work: (tx: BackfillSql) => Promise, + write = false + ): Promise => + withDatabaseRetry( + async () => { + await assertLock() + const result = await sql.begin(async (tx) => { + await configureTransaction(tx) + const value = await work(tx) + await assertLock() + return value + }) + return result as T + }, + write, + report, + stopped + ) + await checkpoint() + if (source) { + for (const family of source.plan) { + if (stopped()) break + const outcome = await transaction((tx) => applyFamily(tx, family, maximum), true) + report.scanned += family.workspaceIds.length + report.families++ + if (outcome.conflict) report.conflicts.push(outcome.conflict) + report.createdProjects += outcome.created + report.assignedWorkspaces += outcome.assigned + if (!outcome.conflict && outcome.assigned === 0) report.unchangedFamilies++ + await checkpoint() + } + report.completed = report.families === source.plan.length + } else { + let afterId = '' + for (;;) { + if (stopped()) break + const page = await transaction( + (tx) => + tx< + { id: string }[] + >`SELECT id FROM workspace WHERE id > ${afterId} ORDER BY id LIMIT 100` + ) + if (!page.length) { + report.completed = true + break + } + for (const candidate of page) { + if (stopped()) break + if (++report.scanned > MAX_WORKSPACES) throw new Error('Workspace scan limit exceeded') + const discovery = await transaction(async (tx) => { + const [root] = await tx<{ id: string; parent: string | null; cycle: boolean }[]>` + WITH RECURSIVE ancestors AS ( + SELECT id, forked_from_workspace_id AS parent, ARRAY[id] AS path, false AS cycle, 0 AS depth FROM workspace WHERE id = ${candidate.id} + UNION ALL + SELECT w.id, w.forked_from_workspace_id, a.path || w.id, w.id = ANY(a.path), a.depth + 1 + FROM workspace w JOIN ancestors a ON w.id = a.parent WHERE NOT a.cycle AND a.depth < 1000 + ) SELECT id, parent, cycle FROM ancestors ORDER BY depth DESC LIMIT 1 + ` + if (!root || root.cycle || root.parent !== null) + return { + conflict: conflict( + candidate.id, + 'Cycle, missing parent, or lineage depth exceeds 1000', + [candidate.id] + ), + } + if (root.id !== candidate.id) return null + return inspectFamily(tx, root.id, maximum) + }) + if (discovery) { + if (discovery.conflict) report.conflicts.push(discovery.conflict) + else { + report.families++ + const suffix = ' - Project' + report.plan.push({ + rootId: candidate.id, + projectId: discovery.existingId ?? generateId(), + workspaceIds: discovery.ids, + fingerprint: fingerprint(discovery.family), + name: `${truncate(discovery.root.name.trim() || 'Untitled', 100 - suffix.length, '')}${suffix}`, + }) + report.createdProjects += discovery.existingId ? 0 : 1 + report.assignedWorkspaces += discovery.missing + if (discovery.missing === 0) report.unchangedFamilies++ + } + } + afterId = candidate.id + } + await checkpoint() + } + if (report.completed) report.conflicts.push(...(await transaction(inspectGlobalState))) + } + report.ready = + options.mode === 'verify' && + report.completed && + !report.conflicts.length && + report.assignedWorkspaces === 0 + if (!report.completed) + report.stopped = 'Interrupted or runtime budget exhausted; rerun from the same plan' + report.finishedAt = new Date().toISOString() + await checkpoint() + return report + } catch (error) { + report.completed = false + report.ready = false + report.errorCode = getPostgresErrorCode(error) + report.stopped = report.errorCode + ? `Database operation failed (${report.errorCode}); reconcile before retrying writes` + : 'Run failed; inspect operator logs and last checkpoint' + report.finishedAt = new Date().toISOString() + await checkpoint() + throw error + } finally { + try { + if (locked && !lockLost) + await connection`SELECT pg_advisory_unlock(hashtextextended(${RUN_LOCK}, 0))` + } finally { + connection.release() + await lockClient.end({ timeout: 2 }) + } + } +} diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 23283468902..ad9cf477872 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2058,6 +2058,55 @@ export const workspace = pgTable( }) ) +/** Stable owner of environments and project-wide resources, independent of fork lineage. */ +export const project = pgTable( + 'project', + { + id: text('id').primaryKey(), + name: text('name').notNull(), + organizationId: text('organization_id').references(() => organization.id, { + onDelete: 'restrict', + }), + /** Lifecycle owner for personal and organization Projects; never an implicit access grant. */ + ownerId: text('owner_id') + .notNull() + .references(() => user.id, { onDelete: 'restrict' }), + archivedAt: timestamp('archived_at'), + createdAt: timestamp('created_at').notNull().defaultNow(), + updatedAt: timestamp('updated_at').notNull().defaultNow(), + }, + (table) => ({ + nameLength: check( + 'project_name_length', + sql`char_length(btrim(${table.name})) BETWEEN 1 AND 100` + ), + organizationIdx: index('project_organization_archive_id_idx').on( + table.organizationId, + table.archivedAt, + table.id + ), + ownerIdx: index('project_owner_archive_id_idx').on(table.ownerId, table.archivedAt, table.id), + }) +) + +// contract-pending(after project writers are fully deployed and backfill validates): enforce exactly-one membership and active Project environment minimums at commit. +export const projectWorkspace = pgTable( + 'project_workspace', + { + projectId: text('project_id') + .notNull() + .references(() => project.id, { onDelete: 'restrict' }), + workspaceId: text('workspace_id') + .notNull() + .references(() => workspace.id, { onDelete: 'cascade' }), + createdAt: timestamp('created_at').notNull().defaultNow(), + }, + (table) => ({ + pk: primaryKey({ columns: [table.projectId, table.workspaceId] }), + workspaceUnique: uniqueIndex('project_workspace_workspace_id_unique').on(table.workspaceId), + }) +) + export const workspaceForkResourceTypeEnum = pgEnum('workspace_fork_resource_type', [ 'workflow', 'oauth_credential', diff --git a/packages/db/scripts/backfill-projects.ts b/packages/db/scripts/backfill-projects.ts new file mode 100644 index 00000000000..1512838d2d5 --- /dev/null +++ b/packages/db/scripts/backfill-projects.ts @@ -0,0 +1,168 @@ +#!/usr/bin/env bun + +import { createHash } from 'node:crypto' +import { readFile, rename, stat, writeFile } from 'node:fs/promises' +import { resolve } from 'node:path' +import { parseArgs } from 'node:util' +import { backfillProjects, readProjectBackfillPlan } from '@sim/db/project-backfill' +import { createLogger } from '@sim/logger' +import { getErrorMessage, getPostgresErrorCode } from '@sim/utils/errors' +import postgres from 'postgres' + +const logger = createLogger('ProjectBackfillCommand', { enabled: true, logLevel: 'INFO' }) +const HELP = `Operator-only Project backfill. Deployment only creates the additive tables. + +bun --no-env-file packages/db/scripts/backfill-projects.ts [options] + +Commands: + identity Print the non-secret database fingerprint; no database writes. + dry-run Discover and validate families; write the plan/report. + apply Apply --from-file PLAN from a completed, conflict-free dry-run. + verify Independently reconcile all environments and Projects; nonzero unless ready. + +Required environment: PROJECT_BACKFILL_DATABASE_URL (direct primary connection). +Reports: PROJECT_BACKFILL_REPORT_PATH (required except identity). +Apply requires --writers-drained and --database-id FINGERPRINT. +Options: --from-file PATH, --seconds 600 (1–3600), --max-family-size 1000 (1–10000). +Run with compatible writers deployed. Pause lineage/ownership writes during apply. +Short NOWAIT table locks exclude concurrent changes during each family transaction. +Interrupted/failed apply: inspect the report, then rerun the SAME dry-run artifact. +Connection loss during writes is never automatically retried; verify the committed state. +No run enables Project consumers or installs the deferred contract constraints.` + +async function main() { + const { positionals, values } = parseArgs({ + args: process.argv.slice(2), + allowPositionals: true, + options: { + help: { type: 'boolean' }, + 'from-file': { type: 'string' }, + 'writers-drained': { type: 'boolean' }, + 'database-id': { type: 'string' }, + seconds: { type: 'string', default: '600' }, + 'max-family-size': { type: 'string', default: '1000' }, + }, + }) + if (values.help) { + process.stdout.write(`${HELP}\n`) + return + } + const mode = positionals[0] ?? 'dry-run' + if (positionals.length > 1 || !['identity', 'dry-run', 'apply', 'verify'].includes(mode)) + throw new Error('Unknown command; use --help') + const rawUrl = process.env.PROJECT_BACKFILL_DATABASE_URL + if (!rawUrl) + throw new Error('Set PROJECT_BACKFILL_DATABASE_URL explicitly; no application DSN fallback') + const url = new URL(rawUrl) + if ( + !['postgres:', 'postgresql:'].includes(url.protocol) || + !url.hostname || + !url.username || + url.pathname.length < 2 + ) + throw new Error('Expected a direct PostgreSQL database URL') + const databaseId = createHash('sha256') + .update( + JSON.stringify([ + url.hostname.toLowerCase(), + url.port || '5432', + decodeURIComponent(url.pathname), + ]) + ) + .digest('hex') + if (mode === 'identity') { + process.stdout.write(`${databaseId}\n`) + return + } + if (mode !== 'dry-run' && mode !== 'apply' && mode !== 'verify') + throw new Error('Invalid command') + const reportPath = process.env.PROJECT_BACKFILL_REPORT_PATH + if (!reportPath) throw new Error('Set PROJECT_BACKFILL_REPORT_PATH explicitly') + if ( + mode === 'apply' && + (!values['writers-drained'] || values['database-id'] !== databaseId || !values['from-file']) + ) + throw new Error('Apply requires --writers-drained, matching --database-id, and --from-file') + if (mode !== 'apply' && (values['writers-drained'] || values['from-file'])) + throw new Error('Apply-only options used with read-only mode') + const inputPath = values['from-file'] + if (inputPath && resolve(inputPath) === resolve(reportPath)) + throw new Error('Keep the dry-run plan and apply report in separate files') + const plan = inputPath + ? await (async () => { + if ((await stat(inputPath)).size > 32 * 1024 * 1024) + throw new Error('Dry-run artifact exceeds 32 MiB') + return readProjectBackfillPlan(JSON.parse(await readFile(inputPath, 'utf8')), databaseId) + })() + : undefined + const sql = postgres(rawUrl, { + max: 1, + prepare: false, + connect_timeout: 10, + idle_timeout: 0, + max_lifetime: null, + connection: { + application_name: 'sim-project-backfill', + statement_timeout: 30_000, + lock_timeout: 2000, + }, + }) + let interrupted = false + const stop = () => { + interrupted = true + } + process.on('SIGINT', stop) + process.on('SIGTERM', stop) + try { + // Preflight the report destination before opening a transaction that can write. + const partialPath = `${reportPath}.${process.pid}.partial` + await writeFile( + partialPath, + JSON.stringify({ databaseId, mode, completed: false, ready: false, stopped: 'Starting' }), + { mode: 0o600 } + ) + await rename(partialPath, reportPath) + const report = await backfillProjects(sql, { + mode, + databaseId, + plan, + seconds: Number(values.seconds), + maxFamilySize: Number(values['max-family-size']), + shouldStop: () => interrupted, + onProgress: async (progress) => { + await writeFile(partialPath, JSON.stringify(progress, null, 2), { mode: 0o600 }) + await rename(partialPath, reportPath) + logger.info('Project backfill progress', { + mode, + databaseId, + scanned: progress.scanned, + families: progress.families, + createdProjects: progress.createdProjects, + assignedWorkspaces: progress.assignedWorkspaces, + conflicts: progress.conflicts.length, + retries: progress.retries, + completed: progress.completed, + }) + }, + }) + if (!report.completed || report.conflicts.length || (mode === 'verify' && !report.ready)) + process.exitCode = 1 + } finally { + process.off('SIGINT', stop) + process.off('SIGTERM', stop) + await sql.end({ timeout: 5 }) + } +} + +try { + await main() +} catch (error) { + const code = getPostgresErrorCode(error) + // Database messages can contain connection details or row values; retain SQLSTATE, not the payload. + logger.error('Project backfill failed', { + error: code + ? `Database error ${code}; inspect the report and reconcile before retrying` + : getErrorMessage(error), + }) + process.exitCode = 1 +} diff --git a/packages/db/scripts/project-backfill.integration.ts b/packages/db/scripts/project-backfill.integration.ts new file mode 100644 index 00000000000..b60b960c7da --- /dev/null +++ b/packages/db/scripts/project-backfill.integration.ts @@ -0,0 +1,396 @@ +import { spawn } from 'node:child_process' +import { readFile } from 'node:fs/promises' +import { backfillProjects, type ProjectBackfillReport } from '@sim/db/project-backfill' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { generateId } from '@sim/utils/id' +import postgres, { type Sql } from 'postgres' +import { describe, expect, it } from 'vitest' + +/** Isolated pre-Project fixture; applies the actual additive migration, never a schema mock. */ +async function withDatabase(run: (sql: Sql, url: string) => Promise) { + const admin = postgres(readTestDatabaseUrl(), { max: 1 }) + const database = `project_backfill_test_${generateId().replaceAll('-', '')}` + const url = new URL(readTestDatabaseUrl()) + url.pathname = `/${database}` + await admin.unsafe(`CREATE DATABASE "${database}"`) + const sql = postgres(url.toString(), { max: 1, onnotice: () => undefined }) + try { + await sql.unsafe(` + CREATE TABLE "user" (id text PRIMARY KEY); + CREATE TABLE organization (id text PRIMARY KEY); + CREATE TABLE workspace ( + id text PRIMARY KEY, name text NOT NULL, owner_id text NOT NULL, + organization_id text, archived_at timestamp, forked_from_workspace_id text + ); + INSERT INTO "user" VALUES ('owner'), ('other-owner'); + INSERT INTO organization VALUES ('org-a'), ('org-b'); + `) + await sql.unsafe( + await readFile(new URL('../migrations/0393_project_foundation.sql', import.meta.url), 'utf8') + ) + await run(sql, url.toString()) + } finally { + await sql.end({ timeout: 2 }) + await admin.unsafe(`DROP DATABASE "${database}" WITH (FORCE)`) + await admin.end() + } +} + +async function seed(sql: Sql) { + await sql`INSERT INTO workspace (id, name, owner_id, organization_id, forked_from_workspace_id, archived_at) VALUES + ('a-root', 'Production', 'owner', 'org-a', NULL, NULL), + ('b-child', 'Staging', 'other-owner', 'org-a', 'a-root', NULL), + ('c-grandchild', 'Dev', 'owner', 'org-a', 'b-child', NULL), + ('d-detached', 'Staging', 'owner', NULL, NULL, NULL), + ('e-detached-child', 'Dev', 'owner', NULL, 'd-detached', NULL), + ('f-archived', 'Archive', 'owner', NULL, NULL, '2025-01-01'), + ('g-archived-child', 'Archive child', 'owner', NULL, 'f-archived', '2025-02-01')` +} + +const databaseId = 'local-test-database' +const dryRun = (sql: Sql) => backfillProjects(sql, { mode: 'dry-run', databaseId }) +const verify = (sql: Sql) => backfillProjects(sql, { mode: 'verify', databaseId }) +const apply = (sql: Sql, plan: ProjectBackfillReport) => + backfillProjects(sql, { mode: 'apply', databaseId, plan }) + +describe('Project backfill operator lifecycle', () => { + it('plans roots and detached families, retains archive timestamps, and replays fixed IDs', async () => { + await withDatabase(async (sql) => { + await seed(sql) + const plan = await dryRun(sql) + expect(plan.createdProjects).toBe(3) + expect(plan.assignedWorkspaces).toBe(7) + expect(await sql`SELECT * FROM project`).toHaveLength(0) + expect((await verify(sql)).ready).toBe(false) + const first = await apply(sql, plan) + expect(first.assignedWorkspaces).toBe(7) + expect((await verify(sql)).ready).toBe(true) + const rows = await sql`SELECT id, name, owner_id, archived_at FROM project ORDER BY name` + expect(rows.map((row) => row.name)).toEqual([ + 'Archive - Project', + 'Production - Project', + 'Staging - Project', + ]) + expect( + ( + await sql`SELECT archived_at::text AS value FROM project WHERE name = 'Archive - Project'` + )[0].value + ).toBe('2025-02-01 00:00:00') + expect(rows[1].owner_id).toBe('owner') + const repeat = await apply(sql, plan) + expect(repeat.createdProjects).toBe(0) + expect(repeat.unchangedFamilies).toBe(3) + expect(await sql`SELECT id, name, owner_id, archived_at FROM project ORDER BY name`).toEqual( + rows + ) + }) + }) + + it('completes partial assignments but blocks split Projects, cycles, missing parents and mixed organizations', async () => { + await withDatabase(async (sql) => { + await seed(sql) + await sql`INSERT INTO project (id, name, owner_id, organization_id) VALUES ('existing', 'Keep this name', 'owner', 'org-a')` + await sql`INSERT INTO project_workspace VALUES ('existing', 'a-root', now())` + const plan = await dryRun(sql) + await apply(sql, plan) + expect( + await sql`SELECT workspace_id FROM project_workspace WHERE project_id = 'existing'` + ).toHaveLength(3) + expect((await sql`SELECT name FROM project WHERE id = 'existing'`)[0].name).toBe( + 'Keep this name' + ) + await sql`INSERT INTO project (id, name, owner_id, organization_id) VALUES ('split', 'Split', 'owner', 'org-a'), ('empty', 'Empty', 'owner', NULL)` + await sql`UPDATE project_workspace SET project_id = 'split' WHERE workspace_id = 'b-child'` + await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) VALUES ('cycle-a', 'A', 'owner', 'cycle-b'), ('cycle-b', 'B', 'owner', 'cycle-a'), ('orphan', 'Orphan', 'owner', 'missing')` + await sql`UPDATE workspace SET organization_id = 'org-b' WHERE id = 'e-detached-child'` + const broken = await verify(sql) + expect(broken.ready).toBe(false) + expect(broken.conflicts.map((row) => row.reason)).toEqual( + expect.arrayContaining([ + 'Family spans multiple Projects', + 'Family spans organizations', + 'Empty Project', + 'Cycle, missing parent, or lineage depth exceeds 1000', + ]) + ) + expect(broken.conflicts.find((row) => row.workspaceId === 'a-root')?.projectIds).toEqual([ + 'existing', + 'split', + ]) + await expect(apply(sql, { ...broken, mode: 'dry-run' })).rejects.toThrow('conflict-free') + }) + }) + + it('rejects a new descendant added after dry-run without assigning a partial family', async () => { + await withDatabase(async (sql) => { + await seed(sql) + const plan = await dryRun(sql) + await sql`INSERT INTO workspace (id, name, owner_id, organization_id, forked_from_workspace_id) VALUES ('late-child', 'Late', 'owner', 'org-a', 'b-child')` + const result = await apply(sql, plan) + expect( + result.conflicts.some( + (row) => row.workspaceId === 'a-root' && row.reason.includes('changed since dry-run') + ) + ).toBe(true) + expect( + await sql`SELECT * FROM project_workspace WHERE workspace_id IN ('a-root', 'b-child', 'c-grandchild', 'late-child')` + ).toHaveLength(0) + const fresh = await dryRun(sql) + await apply(sql, fresh) + expect((await verify(sql)).ready).toBe(true) + }) + }) + + it('resumes after a committed-family interruption and retains progress when reporting fails', async () => { + await withDatabase(async (sql) => { + await seed(sql) + const plan = await dryRun(sql) + let stop = false + const partial = await backfillProjects(sql, { + mode: 'apply', + databaseId, + plan, + shouldStop: () => stop, + onProgress: async (report) => { + if (report.families === 1) stop = true + }, + }) + expect(partial.completed).toBe(false) + expect(partial.createdProjects).toBe(1) + const firstId = (await sql`SELECT id FROM project`)[0].id + await expect( + backfillProjects(sql, { + mode: 'apply', + databaseId, + plan, + onProgress: async (report) => { + if (report.createdProjects > 0) throw new Error('Report disk unavailable') + }, + }) + ).rejects.toThrow('Report disk unavailable') + expect(await sql`SELECT id FROM project`).toHaveLength(2) + await apply(sql, plan) + expect(await sql`SELECT id FROM project WHERE id = ${firstId}`).toHaveLength(1) + expect((await verify(sql)).ready).toBe(true) + }) + }) + + it('refuses another runner while a run owns the lock and stops on a lost database session', async () => { + await withDatabase(async (sql, url) => { + await seed(sql) + const other = postgres(url, { max: 1 }) + try { + let checked = false + await backfillProjects(sql, { + mode: 'dry-run', + databaseId, + onProgress: async () => { + if (checked) return + checked = true + await expect(dryRun(other)).rejects.toThrow('maintenance lock') + }, + }) + const plan = await dryRun(sql) + let terminated = false + let snapshot: ProjectBackfillReport | undefined + await expect( + backfillProjects(sql, { + mode: 'apply', + databaseId, + plan, + onProgress: async (report) => { + snapshot = structuredClone(report) + if (terminated || report.families !== 1) return + terminated = true + await other`SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND pid <> pg_backend_pid()` + }, + }) + ).rejects.toThrow() + expect(snapshot?.completed).toBe(false) + expect(snapshot?.createdProjects).toBe(1) + await apply(other, plan) + expect((await verify(other)).ready).toBe(true) + } finally { + await other.end({ timeout: 2 }) + } + }) + }) + + it('fails boundedly behind a live writer, then succeeds after the writer finishes', async () => { + await withDatabase(async (sql, url) => { + await seed(sql) + const plan = await dryRun(sql) + const writer = postgres(url, { max: 1 }) + try { + await writer.begin(async (tx) => { + await tx`UPDATE workspace SET name = name WHERE id = 'a-root'` + let last: ProjectBackfillReport | undefined + await expect( + backfillProjects(sql, { + mode: 'apply', + databaseId, + plan, + onProgress: async (report) => { + last = structuredClone(report) + }, + }) + ).rejects.toMatchObject({ code: '55P03' }) + expect(last?.retries).toBe(2) + expect(last?.createdProjects).toBe(0) + }) + await apply(sql, plan) + expect((await verify(sql)).ready).toBe(true) + } finally { + await writer.end() + } + }) + }) + + it('retries rolled-back serialization failures but stops on cancellation without partial writes', async () => { + await withDatabase(async (sql) => { + await seed(sql) + const plan = await dryRun(sql) + await sql.unsafe(` + CREATE SEQUENCE attempts; + CREATE FUNCTION fail_twice() RETURNS trigger LANGUAGE plpgsql AS $$ + BEGIN + IF nextval('attempts') <= 2 THEN RAISE EXCEPTION 'injected serialization failure' USING ERRCODE = '40001'; END IF; + RETURN NEW; + END $$; + CREATE TRIGGER fail_twice BEFORE INSERT ON project FOR EACH ROW EXECUTE FUNCTION fail_twice(); + `) + const result = await apply(sql, plan) + expect(result.retries).toBe(2) + expect(result.createdProjects).toBe(3) + expect((await verify(sql)).ready).toBe(true) + await sql`DELETE FROM project_workspace` + await sql`DELETE FROM project` + await sql.unsafe(` + DROP TRIGGER fail_twice ON project; + CREATE FUNCTION cancel_write() RETURNS trigger LANGUAGE plpgsql AS $$ + BEGIN RAISE EXCEPTION 'injected cancellation' USING ERRCODE = '57014'; END $$; + CREATE TRIGGER cancel_write BEFORE INSERT ON project FOR EACH ROW EXECUTE FUNCTION cancel_write(); + `) + let report: ProjectBackfillReport | undefined + await expect( + backfillProjects(sql, { + mode: 'apply', + databaseId, + plan, + onProgress: async (progress) => { + report = structuredClone(progress) + }, + }) + ).rejects.toMatchObject({ code: '57014' }) + expect(report?.retries).toBe(0) + expect(report?.errorCode).toBe('57014') + expect(await sql`SELECT * FROM project_workspace`).toHaveLength(0) + expect(await sql`SELECT * FROM project`).toHaveLength(0) + }) + }) + + it('fails readiness for empty Projects and archived-state mismatch without mutating them', async () => { + await withDatabase(async (sql) => { + await seed(sql) + await apply(sql, await dryRun(sql)) + await sql`UPDATE project SET archived_at = now() WHERE name = 'Production - Project'` + await sql`INSERT INTO project (id, name, owner_id) VALUES ('empty', 'Empty', 'owner')` + const result = await verify(sql) + expect(result.ready).toBe(false) + expect(result.conflicts.map((row) => row.reason)).toEqual( + expect.arrayContaining(['Empty Project', 'Project archive state differs']) + ) + expect(await sql`SELECT id FROM project WHERE id = 'empty'`).toHaveLength(1) + }) + }) +}) + +/** CLI cases exercise the normal connection contract against an isolated database. */ +describe('Project backfill CLI', () => { + it('runs dry-run/apply/verify with durable reports and refuses a mismatched target', async () => { + const { mkdtemp, rm } = await import('node:fs/promises') + const { tmpdir } = await import('node:os') + const { join } = await import('node:path') + await withDatabase(async (sql, url) => { + await seed(sql) + const directory = await mkdtemp(join(tmpdir(), 'project-backfill-test-')) + const planPath = join(directory, 'plan.json') + const reportPath = join(directory, 'report.json') + const run = async (args: string[], report = reportPath) => { + const child = spawn('bun', ['--no-env-file', 'scripts/backfill-projects.ts', ...args], { + env: { + ...process.env, + PROJECT_BACKFILL_DATABASE_URL: url, + PROJECT_BACKFILL_REPORT_PATH: report, + }, + stdio: ['ignore', 'pipe', 'pipe'], + }) + let stdout = '' + let stderr = '' + child.stdout.setEncoding('utf8').on('data', (chunk: string) => { + stdout += chunk + }) + child.stderr.setEncoding('utf8').on('data', (chunk: string) => { + stderr += chunk + }) + const code = await new Promise((resolve, reject) => { + child.on('error', reject) + child.on('close', resolve) + }) + expect(stdout + stderr).not.toContain(url) + return { stdout, stderr, code } + } + try { + const dry = await run(['dry-run'], planPath) + expect(dry.code, dry.stdout + dry.stderr).toBe(0) + const plan = JSON.parse(await readFile(planPath, 'utf8')) as ProjectBackfillReport + expect( + ( + await run([ + 'apply', + '--from-file', + planPath, + '--writers-drained', + '--database-id', + 'wrong', + ]) + ).code + ).toBe(1) + expect(await sql`SELECT * FROM project`).toHaveLength(0) + expect( + ( + await run([ + 'apply', + '--from-file', + planPath, + '--writers-drained', + '--database-id', + plan.databaseId, + ]) + ).code + ).toBe(0) + expect((await run(['verify'])).code).toBe(0) + const report = JSON.parse(await readFile(reportPath, 'utf8')) as ProjectBackfillReport + expect(report.ready).toBe(true) + expect( + ( + await run( + [ + 'apply', + '--from-file', + planPath, + '--writers-drained', + '--database-id', + plan.databaseId, + ], + planPath + ) + ).code + ).toBe(1) + expect(JSON.parse(await readFile(planPath, 'utf8')).mode).toBe('dry-run') + } finally { + await rm(directory, { recursive: true, force: true }) + } + }) + }) +}) diff --git a/packages/sim-cli/src/generated/v2-api.ts b/packages/sim-cli/src/generated/v2-api.ts index 53a4b04bd42..0dbe9171714 100644 --- a/packages/sim-cli/src/generated/v2-api.ts +++ b/packages/sim-cli/src/generated/v2-api.ts @@ -2589,6 +2589,7 @@ export type CreatePermissionGroupBody = { hideSandboxesTab?: boolean disableOAuthAppAccess?: boolean disableKnowledgeBaseExport?: boolean + deniedPartialAccessProjectIssues?: Array } isDefault?: boolean workspaceIds?: Array @@ -2642,6 +2643,7 @@ type CreatePermissionGroupResponseRef0 = { hideSandboxesTab: boolean disableOAuthAppAccess: boolean disableKnowledgeBaseExport: boolean + deniedPartialAccessProjectIssues: Array } isDefault: boolean membershipMode: string @@ -6127,6 +6129,7 @@ type GetPermissionGroupResponseRef0 = { hideSandboxesTab: boolean disableOAuthAppAccess: boolean disableKnowledgeBaseExport: boolean + deniedPartialAccessProjectIssues: Array } isDefault: boolean membershipMode: string @@ -7274,6 +7277,7 @@ type GetWorkspacePermissionConfigResponseRef0 = { hideSandboxesTab: boolean disableOAuthAppAccess: boolean disableKnowledgeBaseExport: boolean + deniedPartialAccessProjectIssues: Array } | null entitled: boolean organizationId: string | null @@ -8951,6 +8955,7 @@ type ListPermissionGroupsResponseRef0 = { hideSandboxesTab: boolean disableOAuthAppAccess: boolean disableKnowledgeBaseExport: boolean + deniedPartialAccessProjectIssues: Array } isDefault: boolean membershipMode: string @@ -10049,6 +10054,7 @@ type PreviewOrganizationAccessRequestResponseRef0 = { | 'hideSandboxesTab' | 'disableOAuthAppAccess' | 'disableKnowledgeBaseExport' + | 'deniedPartialAccessProjectIssues' label: string before: boolean | Array | null after: boolean | Array | null @@ -10205,6 +10211,7 @@ type PreviewOrganizationAccessRequestResponseRef1 = { | 'hideSandboxesTab' | 'disableOAuthAppAccess' | 'disableKnowledgeBaseExport' + | 'deniedPartialAccessProjectIssues' label: string before: boolean | Array | null after: boolean | Array | null @@ -13195,6 +13202,7 @@ export type UpdatePermissionGroupBody = { hideSandboxesTab?: boolean disableOAuthAppAccess?: boolean disableKnowledgeBaseExport?: boolean + deniedPartialAccessProjectIssues?: Array } isDefault?: boolean workspaceIds?: Array @@ -13248,6 +13256,7 @@ type UpdatePermissionGroupResponseRef0 = { hideSandboxesTab: boolean disableOAuthAppAccess: boolean disableKnowledgeBaseExport: boolean + deniedPartialAccessProjectIssues: Array } isDefault: boolean membershipMode: string diff --git a/packages/testing/src/mocks/schema-tables.generated.ts b/packages/testing/src/mocks/schema-tables.generated.ts index c13f3a238c3..fb2e51d7ce4 100644 --- a/packages/testing/src/mocks/schema-tables.generated.ts +++ b/packages/testing/src/mocks/schema-tables.generated.ts @@ -501,6 +501,8 @@ export const GENERATED_SCHEMA_TABLES = { 'createdAt', 'updatedAt', ], + project: ['id', 'name', 'organizationId', 'ownerId', 'archivedAt', 'createdAt', 'updatedAt'], + projectWorkspace: ['projectId', 'workspaceId', 'createdAt'], workspaceForkResourceMap: [ 'id', 'childWorkspaceId', From aa9b41577f456b232b74c2a013c92a6cb00f5b27 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 01:35:39 -0700 Subject: [PATCH 002/113] feat(projects): create projects with their initial environment --- apps/sim/app/api/projects/route.ts | 14 +- apps/sim/lib/api/contracts/projects.ts | 15 ++ .../__integration__/foundation.integration.ts | 172 +++++++++++++++++- .../lib/projects/application/authorization.ts | 2 +- .../projects/application/create-project.ts | 126 +++++++++++++ apps/sim/lib/projects/application/index.ts | 1 + .../lib/projects/application/operations.ts | 7 +- apps/sim/lib/projects/create-input.ts | 8 + apps/sim/lib/projects/membership.ts | 3 +- apps/sim/lib/workspaces/create.ts | 39 ++-- docs/plans/project-entity-foundation.md | 5 +- packages/audit/src/types.ts | 1 + 12 files changed, 371 insertions(+), 22 deletions(-) create mode 100644 apps/sim/lib/projects/application/create-project.ts create mode 100644 apps/sim/lib/projects/create-input.ts diff --git a/apps/sim/app/api/projects/route.ts b/apps/sim/app/api/projects/route.ts index fe0aca010ca..852cc6f3a4c 100644 --- a/apps/sim/app/api/projects/route.ts +++ b/apps/sim/app/api/projects/route.ts @@ -1,11 +1,11 @@ -import { listProjectsContract } from '@/lib/api/contracts/projects' +import { createProjectContract, listProjectsContract } from '@/lib/api/contracts/projects' import { defineInternalJsonRoute, internalOrchestrationErrorPolicy, internalRateLimits, internalSessionAuth, } from '@/lib/api/server/routes' -import { listProjects, projectOperations } from '@/lib/projects/application' +import { createProject, listProjects, projectOperations } from '@/lib/projects/application' export const GET = defineInternalJsonRoute({ contract: listProjectsContract, @@ -16,3 +16,13 @@ export const GET = defineInternalJsonRoute({ mapInput: ({ query }) => query, useCase: listProjects, }) + +export const POST = defineInternalJsonRoute({ + contract: createProjectContract, + auth: internalSessionAuth, + operation: projectOperations.create, + rateLimit: internalRateLimits.user({ bucketName: 'projects.write' }), + errorPolicy: internalOrchestrationErrorPolicy, + mapInput: ({ body }) => body, + useCase: createProject, +}) diff --git a/apps/sim/lib/api/contracts/projects.ts b/apps/sim/lib/api/contracts/projects.ts index 85e774a9cd6..f25549855e0 100644 --- a/apps/sim/lib/api/contracts/projects.ts +++ b/apps/sim/lib/api/contracts/projects.ts @@ -1,6 +1,7 @@ import { z } from 'zod' import { nonEmptyIdSchema } from '@/lib/api/contracts/primitives' import { defineRouteContract } from '@/lib/api/contracts/types' +import { createProjectInputSchema } from '@/lib/projects/create-input' export const projectEnvironmentSchema = z.object({ id: nonEmptyIdSchema, @@ -84,3 +85,17 @@ export const getWorkspaceProjectContract = defineRouteContract({ params: workspaceProjectParamsSchema, response: { mode: 'json', schema: getProjectResponseSchema }, }) + +export const createProjectBodySchema = createProjectInputSchema +export type CreateProjectBody = z.input +export const createProjectResponseSchema = z.object({ + project: z.object({ id: nonEmptyIdSchema, name: z.string() }), + initialEnvironment: z.object({ id: nonEmptyIdSchema, name: z.string() }), +}) +export type CreateProjectResponse = z.output +export const createProjectContract = defineRouteContract({ + method: 'POST', + path: '/api/projects', + body: createProjectBodySchema, + response: { mode: 'json', status: 201, schema: createProjectResponseSchema }, +}) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 0d8cdd291d5..1d617d62d19 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -11,6 +11,7 @@ import { user, userStats, workflow, + workflowBlocks, workspace, workspaceForkBlockMap, workspaceForkDependentValue, @@ -21,14 +22,15 @@ import { createSessionPrincipal, createWorkspaceApiKeyPrincipal, } from '@sim/testing/factories/principal.factory' -import { getErrorMessage } from '@sim/utils/errors' +import { getErrorMessage, getPostgresErrorCode } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' -import { and, eq, inArray } from 'drizzle-orm' +import { and, eq, inArray, sql } from 'drizzle-orm' import { afterAll, describe, expect, it } from 'vitest' import { removeUserFromOrganization } from '@/lib/billing/organizations/membership' import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' import { archiveProject, + createProject, getProject, getProjectIssueAccess, getWorkspaceProject, @@ -180,6 +182,172 @@ afterAll(async () => { }) describe('Project foundation at the database and application boundary', () => { + check( + 'Project creation commits its named first environment and starter workflow in the requested scope', + async () => { + for (const personal of [false, true]) { + const f = await fixture(true, 1) + const organizationId = personal ? null : f.organizationId + if (!f.organizationId) throw new Error('Missing organization fixture') + await db.insert(member).values({ + id: generateId(), + organizationId: f.organizationId, + userId: f.teammateId, + role: 'admin', + createdAt: new Date(), + }) + const result = await createProject.execute({ + principal: f.teammate, + input: { + organizationId, + name: 'Customer support', + initialEnvironment: { name: 'Production' }, + }, + request, + }) + environments.push(result.initialEnvironment.id) + const [created] = await db.select().from(project).where(eq(project.id, result.project.id)) + expect(created).toMatchObject({ + name: 'Customer support', + organizationId, + ownerId: f.teammateId, + }) + const [env] = await db + .select() + .from(workspace) + .where(eq(workspace.id, result.initialEnvironment.id)) + expect(env).toMatchObject({ + name: 'Production', + organizationId, + ownerId: f.teammateId, + billedAccountUserId: personal ? f.teammateId : f.ownerId, + }) + expect( + await db + .select({ + projectId: projectWorkspace.projectId, + workspaceId: projectWorkspace.workspaceId, + }) + .from(projectWorkspace) + .where(eq(projectWorkspace.projectId, created.id)) + ).toEqual([{ projectId: created.id, workspaceId: env.id }]) + const grants = await db.select().from(permissions).where(eq(permissions.entityId, env.id)) + expect( + grants.map((grant) => ({ userId: grant.userId, permissionType: grant.permissionType })) + ).toEqual( + expect.arrayContaining([ + { userId: f.teammateId, permissionType: 'admin' }, + ...(personal ? [] : [{ userId: f.ownerId, permissionType: 'admin' }]), + ]) + ) + expect(grants).toHaveLength(personal ? 1 : 2) + const workflows = await db.select().from(workflow).where(eq(workflow.workspaceId, env.id)) + expect(workflows).toHaveLength(1) + const blocks = await db + .select() + .from(workflowBlocks) + .where(eq(workflowBlocks.workflowId, workflows[0].id)) + expect(blocks.length).toBeGreaterThan(0) + } + } + ) + + check( + 'Project creation refuses workspace keys and foreign organizations without creating resources', + async () => { + const f = await fixture(true, 1) + const foreign = await fixture(true, 1) + const input = { + organizationId: foreign.organizationId, + name: 'Forbidden project', + initialEnvironment: { name: 'Production' }, + } + await expect( + createProject.execute({ principal: f.owner, input, request }) + ).rejects.toMatchObject({ code: 'forbidden' }) + await expect( + createProject.execute({ + principal: createWorkspaceApiKeyPrincipal({ workspaceId: f.ids[0] }), + input: { ...input, organizationId: f.organizationId }, + request, + }) + ).rejects.toThrow('cannot perform operation') + expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(1) + expect( + await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId)) + ).toHaveLength(1) + } + ) + + check( + 'Project creation cannot bypass the workspace-creation restriction through personal scope', + async () => { + const f = await fixture(true, 1) + if (!f.organizationId) throw new Error('Missing organization fixture') + await db.insert(permissionGroup).values({ + id: generateId(), + organizationId: f.organizationId, + name: 'Creation disabled', + createdBy: f.ownerId, + isDefault: true, + config: { disableWorkspaceCreation: true }, + }) + for (const organizationId of [f.organizationId, null]) { + await expect( + createProject.execute({ + principal: f.owner, + input: { + organizationId, + name: 'Forbidden project', + initialEnvironment: { name: 'Production' }, + }, + request, + }) + ).rejects.toMatchObject({ detailCode: 'PERMISSION_GROUP_CAPABILITY_BLOCKED' }) + } + expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(1) + expect( + await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId)) + ).toHaveLength(1) + } + ) + + check( + 'A starter-workflow failure rolls back the Project, environment and permissions together', + async () => { + const f = await fixture(false, 1) + const constraint = sql.identifier(`project_create_test_${generateId().replaceAll('-', '')}`) + const before = await db.select().from(permissions).where(eq(permissions.userId, f.ownerId)) + await db.execute( + sql`ALTER TABLE ${workflow} ADD CONSTRAINT ${constraint} CHECK (${workflow.userId} <> ${f.ownerId}) NOT VALID`.inlineParams() + ) + try { + await expect( + createProject.execute({ + principal: f.owner, + input: { + organizationId: null, + name: 'Rollback project', + initialEnvironment: { name: 'Rollback environment' }, + }, + request, + }) + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23514') + expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength( + 1 + ) + expect( + await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId)) + ).toHaveLength(1) + expect( + await db.select().from(permissions).where(eq(permissions.userId, f.ownerId)) + ).toEqual(before) + } finally { + await db.execute(sql`ALTER TABLE ${workflow} DROP CONSTRAINT ${constraint}`) + } + } + ) + check('workspace creation and forks commit exactly one Project membership', async () => { const f = await fixture(false, 1) const source = await db.transaction((tx) => diff --git a/apps/sim/lib/projects/application/authorization.ts b/apps/sim/lib/projects/application/authorization.ts index a0cf6b571a4..47ebc4f7b43 100644 --- a/apps/sim/lib/projects/application/authorization.ts +++ b/apps/sim/lib/projects/application/authorization.ts @@ -13,7 +13,7 @@ import { lockProject } from '@/lib/projects/membership' export function requireProjectPrincipal( principal: Principal, - operation: ProjectOperation + operation: Pick ): asserts principal is SessionPrincipal { if (principal.kind !== 'session') throw new PrincipalKindAuthorizationError(principal.kind, operation.id) diff --git a/apps/sim/lib/projects/application/create-project.ts b/apps/sim/lib/projects/application/create-project.ts new file mode 100644 index 00000000000..dec1a3ea376 --- /dev/null +++ b/apps/sim/lib/projects/application/create-project.ts @@ -0,0 +1,126 @@ +import { AuditAction, AuditResourceType } from '@sim/audit' +import { db } from '@sim/db' +import { getPostgresConstraintName, getPostgresErrorCode } from '@sim/utils/errors' +import { recordProjectedUseCaseAuditEntries } from '@/lib/core/application/authorized-workspace-use-case' +import type { OperationUseCase } from '@/lib/core/application/operation' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { refuseCapability } from '@/lib/permission-groups/capabilities' +import { requireProjectPrincipal } from '@/lib/projects/application/authorization' +import { projectOperations } from '@/lib/projects/application/operations' +import { type CreateProjectInput, createProjectInputSchema } from '@/lib/projects/create-input' +import { + createWorkspaceWithProjectInTransaction, + emitWorkspaceCreatedPlatformEvent, +} from '@/lib/workspaces/create' +import { + getWorkspaceCreationPolicy, + WorkspaceCreationCapabilityWithheldError, + WorkspaceCreationContextChangedError, +} from '@/lib/workspaces/policy' + +interface CreatedProject { + project: { id: string; name: string } + initialEnvironment: { id: string; name: string } +} + +export const createProject: OperationUseCase< + typeof projectOperations.create, + CreateProjectInput, + CreatedProject +> = { + operation: projectOperations.create, + async execute({ principal, input, request }) { + requireProjectPrincipal(principal, projectOperations.create) + const parsed = createProjectInputSchema.safeParse(input) + if (!parsed.success) + throw new OrchestrationError( + 'validation', + 'A scope, Project name and initial environment name are required' + ) + const { organizationId, name, initialEnvironment } = parsed.data + const policy = await getWorkspaceCreationPolicy({ + userId: principal.userId, + activeOrganizationId: organizationId, + pinOrganization: true, + }) + if (!policy.canCreate) { + if (policy.blockedReasonCode === 'permission-group-denied') + refuseCapability('workspace.create') + throw new OrchestrationError('forbidden', policy.reason ?? 'Project creation is not allowed') + } + if (policy.organizationId !== organizationId) { + throw new OrchestrationError( + 'forbidden', + 'The requested organization cannot create environments under its current subscription' + ) + } + let created: Awaited> + try { + created = await db.transaction((tx) => + createWorkspaceWithProjectInTransaction(tx, { + userId: principal.userId, + name: initialEnvironment.name, + projectName: name, + organizationId, + workspaceMode: policy.workspaceMode, + billedAccountUserId: policy.billedAccountUserId, + observedOrganizationId: policy.observedOrganizationId, + governingPermissionGroupOrganizationId: policy.governingPermissionGroupOrganizationId, + }) + ) + } catch (error) { + if (error instanceof WorkspaceCreationCapabilityWithheldError) + refuseCapability('workspace.create') + if (error instanceof WorkspaceCreationContextChangedError) + throw new OrchestrationError( + 'conflict', + 'Organization membership changed; retry Project creation' + ) + if (getPostgresErrorCode(error) === '55P03') + throw new OrchestrationError( + 'locked', + 'This organization is being updated; retry Project creation' + ) + if ( + getPostgresErrorCode(error) === '23503' && + getPostgresConstraintName(error) === 'workspace_owner_id_user_id_fk' + ) + throw new OrchestrationError('unauthorized', 'Unauthorized') + throw error + } + const environment = created.workspace + emitWorkspaceCreatedPlatformEvent({ + workspaceId: environment.id, + userId: principal.userId, + name: environment.name, + }) + recordProjectedUseCaseAuditEntries( + projectOperations.create, + environment.id, + principal, + request, + [ + { + action: AuditAction.PROJECT_CREATED, + resourceType: AuditResourceType.PROJECT, + resourceId: created.projectId, + resourceName: name, + description: `Created Project "${name}"`, + }, + { + action: AuditAction.WORKSPACE_CREATED, + resourceType: AuditResourceType.WORKSPACE, + resourceId: environment.id, + resourceName: environment.name, + description: `Created workspace "${environment.name}"`, + metadata: { name: environment.name, workspaceMode: environment.workspaceMode }, + }, + ], + organizationId ?? undefined + ) + return { + project: { id: created.projectId, name }, + initialEnvironment: { id: environment.id, name: environment.name }, + } + }, +} diff --git a/apps/sim/lib/projects/application/index.ts b/apps/sim/lib/projects/application/index.ts index 22b91983164..2f3d714ebde 100644 --- a/apps/sim/lib/projects/application/index.ts +++ b/apps/sim/lib/projects/application/index.ts @@ -1,3 +1,4 @@ +export { createProject } from '@/lib/projects/application/create-project' export { projectOperations } from '@/lib/projects/application/operations' export { archiveProject, diff --git a/apps/sim/lib/projects/application/operations.ts b/apps/sim/lib/projects/application/operations.ts index d95eaa5c6e0..4158cd61008 100644 --- a/apps/sim/lib/projects/application/operations.ts +++ b/apps/sim/lib/projects/application/operations.ts @@ -1,5 +1,5 @@ import type { ApplicationOperation } from '@/lib/core/application/operation' -import { assertOperationCapability } from '@/lib/core/application/operation' +import { assertOperationCapability, defineOperation } from '@/lib/core/application/operation' export interface ProjectOperation extends ApplicationOperation { readonly principalKinds: readonly ['session'] @@ -14,6 +14,11 @@ function defineProjectOperation(operation: O): } export const projectOperations = { + create: defineOperation({ + id: 'projects.create', + principalKinds: ['session'], + capability: 'workspace.create', + }), // permission-group-exempt: navigation exposes only Projects containing accessible environments. list: defineProjectOperation({ id: 'projects.list', diff --git a/apps/sim/lib/projects/create-input.ts b/apps/sim/lib/projects/create-input.ts new file mode 100644 index 00000000000..4eb91f72df2 --- /dev/null +++ b/apps/sim/lib/projects/create-input.ts @@ -0,0 +1,8 @@ +import { z } from 'zod' + +export const createProjectInputSchema = z.object({ + organizationId: z.string().trim().min(1).nullable(), + name: z.string().trim().min(1).max(100), + initialEnvironment: z.object({ name: z.string().trim().min(1).max(100) }), +}) +export type CreateProjectInput = z.output diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index 7a328d71702..cfd08e1a8c8 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -32,12 +32,13 @@ export async function createProjectForWorkspace( organizationId: string | null ownerId: string archivedAt?: Date | null + projectName?: string } ): Promise { const id = generateId() await tx.insert(project).values({ id, - name: generatedProjectName(input.name), + name: input.projectName ?? generatedProjectName(input.name), organizationId: input.organizationId, ownerId: input.ownerId, archivedAt: input.archivedAt ?? null, diff --git a/apps/sim/lib/workspaces/create.ts b/apps/sim/lib/workspaces/create.ts index b8aea562d5a..fd6210426bd 100644 --- a/apps/sim/lib/workspaces/create.ts +++ b/apps/sim/lib/workspaces/create.ts @@ -89,9 +89,10 @@ export interface TransactionalCreateWorkspaceParams extends CreateWorkspaceParam * permission-group advisory lock — before inserting the workspace, owner * permission and optional starter workflow atomically. */ -export async function createWorkspaceInTransaction( +export async function createWorkspaceWithProjectInTransaction( tx: DbTransaction, { + projectName, userId, observedOrganizationId, name, @@ -100,8 +101,8 @@ export async function createWorkspaceInTransaction( workspaceMode, billedAccountUserId, governingPermissionGroupOrganizationId, - }: TransactionalCreateWorkspaceParams -): Promise { + }: TransactionalCreateWorkspaceParams & { projectName?: string } +): Promise<{ projectId: string; workspace: CreatedWorkspace }> { const workspaceId = generateId() const workflowId = generateId() const now = new Date() @@ -130,7 +131,8 @@ export async function createWorkspaceInTransaction( updatedAt: now, }) - await createProjectForWorkspace(tx, { + const projectId = await createProjectForWorkspace(tx, { + projectName, workspaceId, name, organizationId: organizationId ?? null, @@ -186,18 +188,29 @@ export async function createWorkspaceInTransaction( } return { - id: workspaceId, - name, - ownerId: userId, - organizationId, - workspaceMode, - billedAccountUserId: committedBilledAccountUserId, - allowPersonalApiKeys: true, - createdAt: now, - updatedAt: now, + projectId, + workspace: { + id: workspaceId, + name, + ownerId: userId, + organizationId, + workspaceMode, + billedAccountUserId: committedBilledAccountUserId, + allowPersonalApiKeys: true, + createdAt: now, + updatedAt: now, + }, } } +/** Preserves the workspace-only result for existing creation callers. */ +export async function createWorkspaceInTransaction( + tx: DbTransaction, + params: TransactionalCreateWorkspaceParams +): Promise { + return (await createWorkspaceWithProjectInTransaction(tx, params)).workspace +} + /** Creates a workspace through the canonical lock-and-insert transaction. */ export async function createWorkspace(params: CreateWorkspaceParams) { /** diff --git a/docs/plans/project-entity-foundation.md b/docs/plans/project-entity-foundation.md index 4126b389c63..6bb232a0600 100644 --- a/docs/plans/project-entity-foundation.md +++ b/docs/plans/project-entity-foundation.md @@ -71,7 +71,7 @@ Do not add `project_member` for navigation or Issues. Extend the existing permis 2. Environment and Project organization scopes agree. Personal environments within a Project may have different lifecycle owners only if one explicitly selected Project owner governs Project lifecycle; that must not transfer environment ownership or grant access. 3. Fork creation inherits its parent's Project in the same transaction after rechecking current source scope under lock. Existing fork families seed Projects initially. Explicit disconnection splits the subtree into a new Project. Multiple roots can still arise through parent deletion; do not infer Issue movement from root counts alone. 4. Nested forks inherit the same Project; siblings need not have a unique depth. A disconnected fork and every descendant move together to a fresh Project. Never manufacture a new Project from a missing-parent/backfill race. Reevaluate Issue eligibility on both Projects after the split: a previously partial-access user may become full-access. Project-targeted permission-group restrictions also need explicit migration semantics; proposed preserve applicable restrictions on the new Project rather than accidentally resetting configured policy, while acknowledging the all-or-nothing rule now applies to each new environment set. Record policy handling and both Project IDs in the operation audit. Existing Issues stay with the original Project. Explicit user-requested migration through Sim Chat/Mothership must authorize source and destination Projects under one semantic application operation, with atomic/retry-safe movement and audit; it is an Issues feature, not a Project-foundation dependency. -5. Renaming an environment does not rename the Project; rename the Project through its own operation. Backfill and new Project creation use ` - Project`, as specified by the naming contract; workspace names remain untouched. Bound the generated Project name without losing its suffix, with `Untitled` for an empty source; surface any length normalization in the report. Duplicates are allowed and recorded, not merged. +5. Renaming an environment does not rename the Project; rename the Project through its own operation. Backfill and implicit Project creation through legacy workspace creation use ` - Project`; explicit Project creation accepts independent Project and initial environment names. Workspace names remain untouched. Bound the generated Project name without losing its suffix, with `Untitled` for an empty source; surface any length normalization in the report. Duplicates are allowed and recorded, not merged. 6. Individual environment delete/archive must leave at least one active environment in an active Project and at least one member environment overall. Block removal of the last environment; direct hard-delete, account deletion, moves and fork disconnect cannot bypass the invariant. Serialize on the Project so two concurrent removals cannot each see the other as the remaining environment. For disconnect/move, validate both resulting Projects; refuse a split that strands the original as active with no active environment. Coordinated whole-Project lifecycle operations are the explicit exception for active-count checks, not an adapter flag that callers can freely set. 7. Project archive is one authorized compound operation that archives every environment and workflow, including operational deactivation already owned by existing archive paths. Retain membership rows and Issues. Reject new environment/fork/workflow admission into an archived Project. Preserve pre-existing archived states. Current `lib/workspaces/lifecycle.ts` commits workspace/resource archival before calling `archiveWorkflowsForWorkspace`; looping this helper across environments would permit a partially archived Project. Refactor transaction-owning primitives so durable Project/environment/workflow state changes commit atomically, with existing required external effects queued/retried after commit. Failure must not be reported as complete while workflows remain active. Project restore is deferred until a complete inverse exists; do not ship a metadata-only unarchive that leaves no active environments or silently revives resources archived before the Project. Archival does not imply cancellation of already-running executions beyond existing archive semantics unless separately specified. 8. Moving one environment between organizations cannot retain its old Project membership. For initial shipment, refuse a partial Project transfer unless the operation explicitly names a destination/new Project and defines the Issue outcome; preserve original Project Issues. A whole-Project transfer requires authorization and disclosure for all environments, retained Project data, collaborators and Issues. @@ -272,13 +272,14 @@ The schema contains `project` (`id`, `name`, required `ownerId`, nullable `organ Implemented session routes: +- `POST /api/projects`: creates a named Project and its named first environment atomically, including admin permissions and a starter workflow. Requires explicit `organizationId` (or `null` for personal scope), `name`, and `initialEnvironment.name`; returns both IDs and names with HTTP 201. Uses existing workspace creation eligibility, billing and permission-group rules. - `GET /api/projects`: authorized, cursor-paginated active Project inventory. - `GET /api/projects/[id]`: metadata, accessible active environments, and administration/Issues capabilities. - `GET /api/projects/by-workspace/[workspaceId]`: canonical lookup that also authorizes the requested environment. - `PATCH /api/projects/[id]`: rename. - `DELETE /api/projects/[id]`: compound Project/environment/workflow archive. -Workspace creation creates its Project atomically; fork creation joins the existing Project. These are the creation surfaces in this foundation. A separate empty-Project create, arbitrary environment attachment, restore UI, public-key API, and Issues CRUD are not implemented here. The existing permission-group editor gains the Project Issues restriction; the new Project navigation UI remains a separate consumer. +Workspace creation creates its Project atomically; fork creation joins the existing Project. Existing workspace creation callers retain their response shape and generated Project names. Project creation shares their transaction primitive; it cannot attach an arbitrary environment or create an empty Project. A separate empty-Project create, arbitrary environment attachment, restore UI, public-key API, and Issues CRUD are not implemented here. The existing permission-group editor gains the Project Issues restriction; the new Project navigation UI remains a separate consumer. `deniedPartialAccessProjectIssues` is a Project-ID denylist on existing permission-group config. An empty list allows access. For a partial-access teammate, each accessible active environment resolves its effective group using existing explicit-member/all-members/default precedence. If any of those effective groups denies this Project, all Issues are denied. The selected environment cannot change the answer. Archived environments are excluded from this comparison. Full-access teammates bypass this partial-access restriction. Issue use cases call `authorizeProject` in their own transaction before reading or writing Issue rows; `getProjectIssueAccess` is only a read-only probe. The Issue gate holds the permission-group lock through that transaction, so callers must respect its existing leaf-lock rule. diff --git a/packages/audit/src/types.ts b/packages/audit/src/types.ts index 6505bf2431a..06547294cf5 100644 --- a/packages/audit/src/types.ts +++ b/packages/audit/src/types.ts @@ -218,6 +218,7 @@ export const AuditAction = { WORKFLOW_EXPORTED: 'workflow.exported', WORKSPACE_CREATED: 'workspace.created', + PROJECT_CREATED: 'project.created', PROJECT_UPDATED: 'project.updated', PROJECT_ARCHIVED: 'project.archived', WORKSPACE_UPDATED: 'workspace.updated', From 91cba23a84dd4ca5211f3a90c168620599a2a3c7 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 02:10:54 -0700 Subject: [PATCH 003/113] docs(projects): record project files follow-up --- docs/plans/project-entity-foundation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/plans/project-entity-foundation.md b/docs/plans/project-entity-foundation.md index 6bb232a0600..c798f58d926 100644 --- a/docs/plans/project-entity-foundation.md +++ b/docs/plans/project-entity-foundation.md @@ -310,3 +310,9 @@ bun --no-env-file packages/db/scripts/backfill-projects.ts apply \ Validation artifacts come from `PROJECT_FOUNDATION_REPORT_PATH` (or `apps/sim/test-results/project-foundation.json`) and the integration runner’s JSON report. CI already uploads `apps/sim/test-results/*.json`. The former mock-count workspace lifecycle tests were replaced by real database checks of concurrent removal and retry repair. The dedicated backfill suite executes the exact additive migration in a disposable database before exercising backfill. External webhook/socket/MCP cleanup retains existing best-effort post-commit behavior; durable archive state commits atomically. The operator-run CLI procedure, self-hosted commands, SQLSTATE handling, lock behavior, report interpretation and repair procedure are documented in [the Project backfill runbook](./project-backfill-runbook.md). + +### Follow-up: Project-scoped files + +Project names provide the identifying label; this foundation intentionally omits a separate description field. Shared purpose, goals, and operating guidance belong in Project-owned files, with a designated brief identified by a stable document ID. + +Extend file ownership to Projects in a separate PR, likely with nullable `projectId` on the existing files table and constraints for valid scope combinations. Include authorization, version history/collaboration, storage lifecycle, and Mothership tools/VFS support. Keep environment files restricted to their environments; publishing them to a Project must explicitly account for broader readership. Project-scoped files and brief consumption are not implemented in this foundation. From 0980ad1ffbab165eaa5f877b6aae485567898ed4 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 11:04:36 -0700 Subject: [PATCH 004/113] docs(projects): explain project and workspace creation flows --- apps/sim/lib/projects/README.md | 76 +++++++++++++++++++++++++ docs/plans/project-entity-foundation.md | 2 + 2 files changed, 78 insertions(+) create mode 100644 apps/sim/lib/projects/README.md diff --git a/apps/sim/lib/projects/README.md b/apps/sim/lib/projects/README.md new file mode 100644 index 00000000000..3b95d0a8195 --- /dev/null +++ b/apps/sim/lib/projects/README.md @@ -0,0 +1,76 @@ +# Project creation + +A Project groups environments. An environment is an existing `workspace` record; there is no separate environment table. Every newly created Project starts with an environment. + +## Choose the creation flow + +| Caller | Flow | Result | +| --- | --- | --- | +| New Project onboarding | `POST /api/projects` | Creates a Project and its first environment together, with independently supplied names. | +| Existing workspace creation UI or caller | `POST /api/workspaces` | Creates a workspace and automatically creates its Project, preserving the existing workspace response. | +| Create another environment by forking | Existing workspace fork operation | Creates a child workspace in the source workspace's Project. | + +Both POST endpoints are internal, session-authenticated APIs. `POST /api/projects` is not a public `/api/v2` endpoint and does not accept API-key principals. This foundation does not remove or deprecate existing workspace creation endpoints. + +Do not call both creation endpoints for one onboarding flow: each creates a new workspace and a new Project. Neither endpoint attaches a workspace to an existing Project. A general Project environment-creation endpoint is follow-up work. + +## Create a Project and its first environment + +Use the shared client and contract for same-origin application calls: + +```ts +import { requestJson } from '@/lib/api/client/request' +import { createProjectContract } from '@/lib/api/contracts/projects' + +const result = await requestJson(createProjectContract, { + body: { + organizationId: selectedOrganizationId, + name: 'Customer support', + initialEnvironment: { name: 'Production' }, + }, +}) +``` + +All three inputs are required: + +- `organizationId`: the intended organization's ID, or explicitly `null` for a personal Project. There is no fallback to the session's active organization. The caller must be eligible to create in the requested scope; an ineligible organization request is not silently converted to personal creation. +- `name`: the Project name, trimmed and limited to 1–100 characters. +- `initialEnvironment.name`: the first environment's name, also trimmed and limited to 1–100 characters. There is **no default environment name**; `Production` above is an example supplied by the caller. + +For personal creation, use the same request with `organizationId: null`. + +The HTTP 201 response contains both IDs and names: + +```json +{ + "project": { "id": "", "name": "Customer support" }, + "initialEnvironment": { "id": "", "name": "Production" } +} +``` + +`initialEnvironment.id` is the workspace ID for existing workspace routes and navigation. + +The application operation creates the Project, workspace, Project membership, initial administrator permissions, and starter workflow in one database transaction. A failed transaction leaves none of these new records committed. This endpoint always includes the starter workflow; it has no `skipDefaultWorkflow` option. + +Creation uses existing workspace eligibility, billing, and `workspace.create` permission-group rules. Choosing personal scope does not bypass applicable organization permission-group restrictions. This endpoint has no idempotency-key support: resubmitting after an uncertain network result can create another Project, so do not blindly retry. + +## Existing workspace creation + +Existing callers can continue to use `createWorkspaceContract` and `POST /api/workspaces` with their current body: + +```json +{ + "name": "Support workspace", + "skipDefaultWorkflow": false +} +``` + +`skipDefaultWorkflow` remains optional and defaults to `false`. The route uses the session's active organization and existing workspace creation policy to resolve ownership and billing. It does not take the explicit Project scope or independent Project name used by `POST /api/projects`. + +The workspace and its Project are still created atomically. The generated Project name is `Support workspace - Project`; long names are bounded to 100 characters while retaining the suffix. The response remains `{ "workspace": ... }` with HTTP 200, without a new Project response wrapper. Call `GET /api/projects/by-workspace/[workspaceId]` when an existing workspace caller needs its authorized Project details. + +## Server implementation + +The Project route calls `createProject` in `application/create-project.ts`. Existing workspace callers continue through their current creation paths. Both use the shared transaction primitive in `lib/workspaces/create.ts`; surface adapters must not independently commit Project and workspace creation. + +Project descriptions and Project-scoped files are not part of this creation contract. Project-scoped files and a designated Project brief are follow-up work documented in the foundation plan. diff --git a/docs/plans/project-entity-foundation.md b/docs/plans/project-entity-foundation.md index c798f58d926..f2442f8fb8b 100644 --- a/docs/plans/project-entity-foundation.md +++ b/docs/plans/project-entity-foundation.md @@ -270,6 +270,8 @@ Every Project retains a required `ownerId`, including organization Projects. `or The schema contains `project` (`id`, `name`, required `ownerId`, nullable `organizationId`, `archivedAt`, `createdAt`, `updatedAt`) and `project_workspace` (`projectId`, unique `workspaceId`, `createdAt`). There is no stored fork depth, position, or Project billing state. +For request/response examples and the distinction between explicit Project creation and existing workspace creation, see the [Project creation guide](../../apps/sim/lib/projects/README.md). + Implemented session routes: - `POST /api/projects`: creates a named Project and its named first environment atomically, including admin permissions and a starter workflow. Requires explicit `organizationId` (or `null` for personal scope), `name`, and `initialEnvironment.name`; returns both IDs and names with HTTP 201. Uses existing workspace creation eligibility, billing and permission-group rules. From a14b4489ba40aacf1904ac20be0e3380bf8c3fd2 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 13:20:49 -0700 Subject: [PATCH 005/113] fix(projects): stage activation after compatible writers deploy --- .../ee/workspace-forking/lib/create-fork.ts | 7 +- .../lib/billing/organizations/membership.ts | 5 +- apps/sim/lib/core/config/env.ts | 2 + apps/sim/lib/projects/README.md | 10 +- .../__integration__/foundation.integration.ts | 301 +++++++++++++++- apps/sim/lib/projects/account-deletion.ts | 3 +- .../projects/application/create-project.ts | 2 + .../sim/lib/projects/application/use-cases.ts | 7 + apps/sim/lib/projects/membership.ts | 42 ++- apps/sim/lib/projects/rollout.server.ts | 22 ++ apps/sim/lib/workspaces/create.ts | 42 ++- docs/plans/project-backfill-runbook.md | 68 ---- docs/plans/project-entity-foundation.md | 320 ------------------ packages/db/scripts/backfill-projects.ts | 49 ++- .../scripts/project-backfill.integration.ts | 31 ++ 15 files changed, 489 insertions(+), 422 deletions(-) create mode 100644 apps/sim/lib/projects/rollout.server.ts delete mode 100644 docs/plans/project-backfill-runbook.md delete mode 100644 docs/plans/project-entity-foundation.md diff --git a/apps/sim/ee/workspace-forking/lib/create-fork.ts b/apps/sim/ee/workspace-forking/lib/create-fork.ts index 1192b8985f2..bc73206df66 100644 --- a/apps/sim/ee/workspace-forking/lib/create-fork.ts +++ b/apps/sim/ee/workspace-forking/lib/create-fork.ts @@ -314,9 +314,10 @@ export async function createFork(params: CreateForkParams): Promise { + vi.stubEnv('PROJECT_WRITES_ENABLED', 'true') + vi.stubEnv('PROJECT_API_ENABLED', 'true') +}) + const users: string[] = [] const organizations: string[] = [] const environments: string[] = [] @@ -182,6 +193,292 @@ afterAll(async () => { }) describe('Project foundation at the database and application boundary', () => { + check( + 'disabled rollout preserves legacy creation and fork/disconnect without Project rows', + async () => { + vi.stubEnv('PROJECT_WRITES_ENABLED', 'false') + vi.stubEnv('PROJECT_API_ENABLED', 'false') + const f = await fixture(false, 1) + const source = await db.transaction((tx) => + createWorkspaceInTransaction(tx, { + userId: f.ownerId, + name: 'Legacy source', + organizationId: null, + observedOrganizationId: null, + governingPermissionGroupOrganizationId: null, + workspaceMode: 'personal', + billedAccountUserId: f.ownerId, + skipDefaultWorkflow: true, + }) + ) + environments.push(source.id) + expect( + await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, source.id)) + ).toEqual([]) + const parent = await getWorkspaceWithOwner(source.id) + if (!parent) throw new Error('Missing source fixture') + const fork = await createFork({ + source: parent, + policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), + userId: f.ownerId, + name: 'Legacy child', + }) + environments.push(fork.workspace.id) + expect( + await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) + ).toEqual([]) + await unlinkForkEdge({ parentWorkspaceId: source.id, childWorkspaceId: fork.workspace.id }) + const [child] = await db.select().from(workspace).where(eq(workspace.id, fork.workspace.id)) + expect(child.forkedFromWorkspaceId).toBeNull() + expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(1) + } + ) + + check( + 'Project operations remain unavailable until API activation with no partial creation', + async () => { + const f = await fixture(false, 1) + vi.stubEnv('PROJECT_API_ENABLED', 'false') + for (const writes of ['false', 'true']) { + vi.stubEnv('PROJECT_WRITES_ENABLED', writes) + const input = { projectId: f.projectId } + const calls = [ + () => + createProject.execute({ + principal: f.owner, + input: { + organizationId: null, + name: 'Blocked', + initialEnvironment: { name: 'Production' }, + }, + request, + }), + () => getProject.execute({ principal: f.owner, input, request }), + () => + getWorkspaceProject.execute({ + principal: f.owner, + input: { workspaceId: f.ids[0] }, + request, + }), + () => listProjects.execute({ principal: f.owner, input: { limit: 10 }, request }), + () => + renameProject.execute({ + principal: f.owner, + input: { ...input, name: 'Blocked' }, + request, + }), + () => archiveProject.execute({ principal: f.owner, input, request }), + () => getProjectIssueAccess.execute({ principal: f.owner, input, request }), + ] + for (const call of calls) await expect(call()).rejects.toMatchObject({ statusCode: 503 }) + expect( + await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId)) + ).toHaveLength(1) + const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(record.name).toBe('Environment 0 - Project') + expect(record.archivedAt).toBeNull() + } + } + ) + + check( + 'disabling activation preserves assigned fork membership and lifecycle protections', + async () => { + const f = await fixture(false, 1) + vi.stubEnv('PROJECT_WRITES_ENABLED', 'false') + vi.stubEnv('PROJECT_API_ENABLED', 'false') + const parent = await getWorkspaceWithOwner(f.ids[0]) + if (!parent) throw new Error('Missing source fixture') + const fork = await createFork({ + source: parent, + policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), + userId: f.ownerId, + name: 'Assigned child', + }) + environments.push(fork.workspace.id) + const [membership] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) + expect(membership.projectId).toBe(f.projectId) + await unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: fork.workspace.id }) + const [detached] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) + expect(detached.projectId).not.toBe(f.projectId) + await expect( + archiveWorkspace(fork.workspace.id, { requestId: 'disabled-project-rollout' }) + ).rejects.toMatchObject({ code: 'conflict' }) + } + ) + + check('writer activation assigns new workspaces while Project APIs remain disabled', async () => { + vi.stubEnv('PROJECT_API_ENABLED', 'false') + const f = await fixture(false, 1) + const created = await db.transaction((tx) => + createWorkspaceInTransaction(tx, { + userId: f.ownerId, + name: 'Writer activation', + organizationId: null, + observedOrganizationId: null, + governingPermissionGroupOrganizationId: null, + workspaceMode: 'personal', + billedAccountUserId: f.ownerId, + skipDefaultWorkflow: true, + }) + ) + environments.push(created.id) + expect( + await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, created.id)) + ).toHaveLength(1) + vi.stubEnv('PROJECT_WRITES_ENABLED', 'false') + vi.stubEnv('PROJECT_API_ENABLED', 'true') + await expect( + createProject.execute({ + principal: f.owner, + input: { organizationId: null, name: 'Invalid', initialEnvironment: { name: 'First' } }, + request, + }) + ).rejects.toThrow('PROJECT_API_ENABLED requires PROJECT_WRITES_ENABLED') + expect(await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId))).toHaveLength( + 2 + ) + }) + + check('legacy fork and disconnect refuse a partially assigned subtree', async () => { + const f = await fixture(false, 3) + await db + .delete(projectWorkspace) + .where(inArray(projectWorkspace.workspaceId, f.ids.slice(0, 2))) + vi.stubEnv('PROJECT_WRITES_ENABLED', 'false') + vi.stubEnv('PROJECT_API_ENABLED', 'false') + const parent = await getWorkspaceWithOwner(f.ids[1]) + if (!parent) throw new Error('Missing source fixture') + await expect( + createFork({ + source: parent, + policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), + userId: f.ownerId, + name: 'Invalid child', + }) + ).rejects.toMatchObject({ code: 'conflict' }) + await expect( + unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: f.ids[1] }) + ).rejects.toMatchObject({ code: 'conflict' }) + const [child] = await db.select().from(workspace).where(eq(workspace.id, f.ids[1])) + expect(child.forkedFromWorkspaceId).toBe(f.ids[0]) + expect(await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId))).toHaveLength( + 3 + ) + }) + + check( + 'legacy membership decisions exclude backfill until the writing transaction commits', + async () => { + const f = await fixture(false, 1) + await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + await db.delete(project).where(eq(project.id, f.projectId)) + const read = createDeferred() + const release = createDeferred() + const writer = db.transaction(async (tx) => { + expect(await lockWorkspaceProject(tx, f.ids[0])).toBeNull() + read.resolve() + await release.promise + await tx + .update(workspace) + .set({ name: 'Concurrent legacy edit' }) + .where(eq(workspace.id, f.ids[0])) + }) + try { + await Promise.race([read.promise, writer]) + await expect( + db.transaction(async (tx) => { + await tx.execute( + sql`LOCK TABLE workspace, project, project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT` + ) + }) + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '55P03') + } finally { + release.resolve() + await writer + } + await db.transaction(async (tx) => { + await tx.execute( + sql`LOCK TABLE workspace, project, project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT` + ) + await createProjectForWorkspace(tx, { + workspaceId: f.ids[0], + name: 'Concurrent legacy edit', + organizationId: null, + ownerId: f.ownerId, + }) + }) + expect( + await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + ).toHaveLength(1) + } + ) + + check('a fork waiting for backfill inherits membership committed before it resumes', async () => { + const f = await fixture(false, 1) + await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + await db.delete(project).where(eq(project.id, f.projectId)) + const parent = await getWorkspaceWithOwner(f.ids[0]) + if (!parent) throw new Error('Missing source fixture') + const policy = await getWorkspaceCreationPolicy({ userId: f.ownerId }) + const locked = createDeferred() + const release = createDeferred() + const backfill = db.transaction(async (tx) => { + await tx.execute( + sql`LOCK TABLE workspace, project, project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT` + ) + locked.resolve() + await release.promise + return createProjectForWorkspace(tx, { + workspaceId: f.ids[0], + name: 'Backfilled source', + organizationId: null, + ownerId: f.ownerId, + }) + }) + await Promise.race([locked.promise, backfill]) + const fork = createFork({ + source: parent, + policy, + userId: f.ownerId, + name: 'Concurrent child', + }).then((result) => { + environments.push(result.workspace.id) + return result + }) + let blocked = false + try { + for (let attempt = 0; attempt < 100; attempt++) { + const rows = await db.execute<{ waiting: boolean }>(sql`SELECT EXISTS ( + SELECT 1 FROM pg_locks WHERE relation = 'workspace'::regclass AND mode = 'RowExclusiveLock' AND NOT granted + ) AS waiting`) + if (rows[0]?.waiting) { + blocked = true + break + } + await sleep(20) + } + } finally { + release.resolve() + } + const [projectId, result] = await Promise.all([backfill, fork]) + expect(blocked).toBe(true) + const [membership] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, result.workspace.id)) + expect(membership.projectId).toBe(projectId) + }) + check( 'Project creation commits its named first environment and starter workflow in the requested scope', async () => { diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts index 541ea922767..795c999dd57 100644 --- a/apps/sim/lib/projects/account-deletion.ts +++ b/apps/sim/lib/projects/account-deletion.ts @@ -2,7 +2,7 @@ import { permissions, project, projectWorkspace, workspace } from '@sim/db/schem import { and, asc, eq, inArray, ne, or, sql } from 'drizzle-orm' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbTransaction } from '@/lib/db/types' -import { lockProject } from '@/lib/projects/membership' +import { lockProject, lockProjectBackfillWrites } from '@/lib/projects/membership' /** Account teardown may erase a wholly private Project, but never strand a surviving one. */ export async function prepareProjectsForAccountDeletion( @@ -10,6 +10,7 @@ export async function prepareProjectsForAccountDeletion( userId: string, doomedWorkspaceIds: string[] ): Promise { + await lockProjectBackfillWrites(tx) const records = await tx .select({ id: project.id }) .from(project) diff --git a/apps/sim/lib/projects/application/create-project.ts b/apps/sim/lib/projects/application/create-project.ts index dec1a3ea376..b3d039b8e64 100644 --- a/apps/sim/lib/projects/application/create-project.ts +++ b/apps/sim/lib/projects/application/create-project.ts @@ -8,6 +8,7 @@ import { refuseCapability } from '@/lib/permission-groups/capabilities' import { requireProjectPrincipal } from '@/lib/projects/application/authorization' import { projectOperations } from '@/lib/projects/application/operations' import { type CreateProjectInput, createProjectInputSchema } from '@/lib/projects/create-input' +import { requireProjectApiEnabled } from '@/lib/projects/rollout.server' import { createWorkspaceWithProjectInTransaction, emitWorkspaceCreatedPlatformEvent, @@ -31,6 +32,7 @@ export const createProject: OperationUseCase< operation: projectOperations.create, async execute({ principal, input, request }) { requireProjectPrincipal(principal, projectOperations.create) + requireProjectApiEnabled() const parsed = createProjectInputSchema.safeParse(input) if (!parsed.success) throw new OrchestrationError( diff --git a/apps/sim/lib/projects/application/use-cases.ts b/apps/sim/lib/projects/application/use-cases.ts index d67a2c52b89..eb7484471d8 100644 --- a/apps/sim/lib/projects/application/use-cases.ts +++ b/apps/sim/lib/projects/application/use-cases.ts @@ -8,6 +8,7 @@ import { OrchestrationError } from '@/lib/core/orchestration/types' import { authorizeProject, requireProjectPrincipal } from '@/lib/projects/application/authorization' import { projectOperations } from '@/lib/projects/application/operations' import { archiveProjectInTransaction, finishProjectArchive } from '@/lib/projects/lifecycle' +import { requireProjectApiEnabled } from '@/lib/projects/rollout.server' interface ProjectInput { projectId: string @@ -31,6 +32,7 @@ export const getProject: OperationUseCase< operation: projectOperations.get, async execute({ principal, input }) { requireProjectPrincipal(principal, projectOperations.get) + requireProjectApiEnabled() return db.transaction(async (tx) => ({ project: presentProject(await authorizeProject(tx, principal, projectOperations.get, input)), })) @@ -46,6 +48,7 @@ export const getProjectIssueAccess: OperationUseCase< operation: projectOperations.issues, async execute({ principal, input }) { requireProjectPrincipal(principal, projectOperations.issues) + requireProjectApiEnabled() return db.transaction(async (tx) => { const context = await authorizeProject(tx, principal, projectOperations.issues, input) return { projectId: context.record.id } @@ -61,6 +64,7 @@ export const listProjects: OperationUseCase< operation: projectOperations.list, async execute({ principal, input }) { requireProjectPrincipal(principal, projectOperations.list) + requireProjectApiEnabled() if (!Number.isInteger(input.limit) || input.limit < 1 || input.limit > 100) throw new OrchestrationError('validation', 'Limit must be between 1 and 100') return db.transaction(async (tx) => { @@ -108,6 +112,7 @@ export const renameProject: OperationUseCase< operation: projectOperations.rename, async execute({ principal, input, request }) { requireProjectPrincipal(principal, projectOperations.rename) + requireProjectApiEnabled() const name = input.name.trim() if (!name || name.length > 100) throw new OrchestrationError('validation', 'Project name must contain 1–100 characters') @@ -149,6 +154,7 @@ export const archiveProject: OperationUseCase< operation: projectOperations.archive, async execute({ principal, input, request }) { requireProjectPrincipal(principal, projectOperations.archive) + requireProjectApiEnabled() const result = await db.transaction(async (tx) => { const context = await authorizeProject(tx, principal, projectOperations.archive, input) const effects = await archiveProjectInTransaction(tx, context.record.id) @@ -184,6 +190,7 @@ export const getWorkspaceProject: OperationUseCase< operation: projectOperations.get, async execute({ principal, input }) { requireProjectPrincipal(principal, projectOperations.get) + requireProjectApiEnabled() return db.transaction(async (tx) => { const [membership] = await tx .select({ projectId: projectWorkspace.projectId }) diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index cfd08e1a8c8..54c45c6e36c 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -5,6 +5,18 @@ import { and, asc, eq, inArray, isNull, ne, notInArray, sql } from 'drizzle-orm' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbOrTx, DbTransaction } from '@/lib/db/types' +/** Prevents backfill from assigning membership between an absence read and the ensuing write. */ +export async function lockProjectBackfillWrites(tx: DbTransaction): Promise { + await tx.execute(sql`SET LOCAL lock_timeout = '5s'`) + try { + await tx.execute(sql`LOCK TABLE workspace IN ROW EXCLUSIVE MODE`) + } catch (error) { + if (getPostgresErrorCode(error) === '55P03') + throw new OrchestrationError('conflict', 'Project backfill is running; retry the operation') + throw error + } +} + /** Canonical Project mutex; membership and lifecycle writers hold it until commit. */ export async function lockProject(tx: DbTransaction, projectId: string): Promise { await tx.execute(sql`SELECT set_config('lock_timeout', '5000ms', true)`) @@ -49,6 +61,7 @@ export async function createProjectForWorkspace( /** Returns null only for a legacy workspace awaiting the operator-run backfill. */ export async function lockWorkspaceProject(tx: DbTransaction, workspaceId: string) { + await lockProjectBackfillWrites(tx) const [membership] = await tx .select() .from(projectWorkspace) @@ -70,13 +83,28 @@ export async function lockWorkspaceProject(tx: DbTransaction, workspaceId: strin export async function requireForkProject(tx: DbTransaction, parentWorkspaceId: string) { const parent = await lockWorkspaceProject(tx, parentWorkspaceId) - if (!parent) + if (!parent) { + await requireUnassignedForkSubtree(tx, parentWorkspaceId) + return null + } + if (parent.archivedAt) throw new OrchestrationError('conflict', 'Cannot fork an archived Project') + return parent +} + +/** Legacy fallback must not hide partially assigned descendants. Caller holds the lineage lock. */ +async function requireUnassignedForkSubtree(tx: DbTransaction, workspaceId: string): Promise { + const rows = await tx.execute<{ id: string }>(sql` + WITH RECURSIVE descendants AS ( + SELECT id FROM workspace WHERE id = ${workspaceId} + UNION + SELECT w.id FROM workspace w JOIN descendants d ON w.forked_from_workspace_id = d.id + ) SELECT d.id FROM descendants d JOIN project_workspace pw ON pw.workspace_id = d.id LIMIT 1 + `) + if (rows.length) throw new OrchestrationError( 'conflict', - 'This workspace needs the Project backfill before it can be forked' + 'Fork descendants need Project membership reconciliation' ) - if (parent.archivedAt) throw new OrchestrationError('conflict', 'Cannot fork an archived Project') - return parent } /** Individual removal cannot leave an active Project without an active environment. */ @@ -112,7 +140,10 @@ export async function splitForkProject( workspaceId: string ): Promise { const owner = await lockWorkspaceProject(tx, workspaceId) - if (!owner) return null + if (!owner) { + await requireUnassignedForkSubtree(tx, workspaceId) + return null + } if (owner.archivedAt) throw new OrchestrationError('conflict', 'Cannot disconnect an archived Project') const rows = await tx.execute<{ @@ -187,6 +218,7 @@ export async function transferWorkspaceProjects( ownerId?: string ): Promise { if (!workspaceIds.length) return + await lockProjectBackfillWrites(tx) const owners = await tx .selectDistinct({ id: projectWorkspace.projectId }) .from(projectWorkspace) diff --git a/apps/sim/lib/projects/rollout.server.ts b/apps/sim/lib/projects/rollout.server.ts new file mode 100644 index 00000000000..3ffe5d81e8c --- /dev/null +++ b/apps/sim/lib/projects/rollout.server.ts @@ -0,0 +1,22 @@ +import { envBoolean, getEnv } from '@/lib/core/config/env' +import { HttpError } from '@/lib/core/utils/http-error' + +/** Deployment-wide controls; existing Project lifecycle maintenance never depends on these. */ +export function getProjectRollout() { + const writesEnabled = envBoolean(getEnv('PROJECT_WRITES_ENABLED')) ?? false + const apiEnabled = envBoolean(getEnv('PROJECT_API_ENABLED')) ?? false + if (apiEnabled && !writesEnabled) + throw new Error('PROJECT_API_ENABLED requires PROJECT_WRITES_ENABLED') + return { writesEnabled, apiEnabled } +} + +class ProjectUnavailableError extends HttpError { + readonly statusCode = 503 + constructor() { + super('Projects are not enabled on this deployment') + } +} + +export function requireProjectApiEnabled(): void { + if (!getProjectRollout().apiEnabled) throw new ProjectUnavailableError() +} diff --git a/apps/sim/lib/workspaces/create.ts b/apps/sim/lib/workspaces/create.ts index fd6210426bd..d81847a487f 100644 --- a/apps/sim/lib/workspaces/create.ts +++ b/apps/sim/lib/workspaces/create.ts @@ -6,6 +6,7 @@ import { generateId } from '@sim/utils/id' import { PlatformEvents } from '@/lib/core/telemetry' import type { DbTransaction } from '@/lib/db/types' import { createProjectForWorkspace } from '@/lib/projects/membership' +import { getProjectRollout, requireProjectApiEnabled } from '@/lib/projects/rollout.server' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' @@ -89,10 +90,11 @@ export interface TransactionalCreateWorkspaceParams extends CreateWorkspaceParam * permission-group advisory lock — before inserting the workspace, owner * permission and optional starter workflow atomically. */ -export async function createWorkspaceWithProjectInTransaction( +async function createWorkspaceRecordsInTransaction( tx: DbTransaction, { projectName, + assignProject, userId, observedOrganizationId, name, @@ -101,8 +103,8 @@ export async function createWorkspaceWithProjectInTransaction( workspaceMode, billedAccountUserId, governingPermissionGroupOrganizationId, - }: TransactionalCreateWorkspaceParams & { projectName?: string } -): Promise<{ projectId: string; workspace: CreatedWorkspace }> { + }: TransactionalCreateWorkspaceParams & { projectName?: string; assignProject: boolean } +): Promise<{ projectId: string | null; workspace: CreatedWorkspace }> { const workspaceId = generateId() const workflowId = generateId() const now = new Date() @@ -131,13 +133,15 @@ export async function createWorkspaceWithProjectInTransaction( updatedAt: now, }) - const projectId = await createProjectForWorkspace(tx, { - projectName, - workspaceId, - name, - organizationId: organizationId ?? null, - ownerId: userId, - }) + const projectId = assignProject + ? await createProjectForWorkspace(tx, { + projectName, + workspaceId, + name, + organizationId: organizationId ?? null, + ownerId: userId, + }) + : null const permissionRows = [ { @@ -203,12 +207,28 @@ export async function createWorkspaceWithProjectInTransaction( } } +/** Explicit Project creation always commits its first environment in the same transaction. */ +export async function createWorkspaceWithProjectInTransaction( + tx: DbTransaction, + params: TransactionalCreateWorkspaceParams & { projectName?: string } +): Promise<{ projectId: string; workspace: CreatedWorkspace }> { + requireProjectApiEnabled() + const created = await createWorkspaceRecordsInTransaction(tx, { ...params, assignProject: true }) + if (!created.projectId) throw new Error('Project creation did not assign a Project') + return { ...created, projectId: created.projectId } +} + /** Preserves the workspace-only result for existing creation callers. */ export async function createWorkspaceInTransaction( tx: DbTransaction, params: TransactionalCreateWorkspaceParams ): Promise { - return (await createWorkspaceWithProjectInTransaction(tx, params)).workspace + return ( + await createWorkspaceRecordsInTransaction(tx, { + ...params, + assignProject: getProjectRollout().writesEnabled, + }) + ).workspace } /** Creates a workspace through the canonical lock-and-insert transaction. */ diff --git a/docs/plans/project-backfill-runbook.md b/docs/plans/project-backfill-runbook.md deleted file mode 100644 index d5154830401..00000000000 --- a/docs/plans/project-backfill-runbook.md +++ /dev/null @@ -1,68 +0,0 @@ -# Project backfill operations - -The SQL migration creates empty Project tables during the normal migration job. This backfill is a separate post-deployment operation. It is not registered in the startup migration registry: old replicas are still serving when that registry runs. - -## Operator execution - -Run the CLI from an approved maintenance host or a one-off migration container matching the deployed application revision. There is no dedicated GitHub Actions workflow and no automatic migration-registry entry for this backfill. The normal deployment pipeline still applies the additive SQL migration. - -Before apply: - -- Finish deploying the compatible release, including draining old workers. Pause manual lineage/ownership maintenance and coordinate a window without concurrent deployments. Without a workflow wrapper, the operator owns these checks; the CLI does not verify ECS cutover or serialize deployment pipelines. -- Supply `PROJECT_BACKFILL_DATABASE_URL` through the approved secret mechanism. Use a direct session-capable primary connection, with no pooled application DSN fallback. Verify the environment and the fingerprint printed by `identity`. -- Use a persistent, writable report directory. Retain the exact code revision, dry-run plan, apply reports, verification reports and console output with the release record. Reports contain resource metadata and should have restricted access. -- Rehearse staging using the same command before production. This task has not executed a hosted backfill or established an existing maintenance host. - -1. Run `dry-run` and inspect the JSON report: roots, environment IDs, planned Project IDs, proposed names, changes and conflicts. -2. Repair reported conflicts deliberately and produce a fresh dry run. Apply refuses incomplete or conflicted plans. -3. Run `apply --from-file --database-id --writers-drained`. Keep the same reviewed code revision and original plan for retries. The acknowledgement does not stop writers; each transaction applies one planned family after checking for changes. -4. Run `verify` independently. Require `completed: true`, `ready: true`, zero conflicts and zero missing assignments. Apply success alone does not establish readiness. -5. Repeat verification after normal writes resume. Keep Project/Issues consumers disabled until reconciliation and the separately deployed database membership/minimum-environment constraints are complete. This command never flips an activation flag. - -A canceled or failed apply may have committed earlier families. Retain its report and original dry-run artifact; reapply the same artifact after checking the failure. Existing matching planned IDs are reused. A changed code revision requires reviewing compatibility and normally a new dry-run plan. Never produce a new plan merely to hide an unexpected assignment. - -## Safety and failure handling - -- Dry-run and verify do not change database rows. All modes take a session advisory lock so two operator runs cannot overlap. The reserved connection and backend PID are checked before each batch; losing that connection stops the run. -- Discovery pages contain 100 workspace IDs. Each family defaults to at most 1,000 members at the CLI, with an explicit maximum of 10,000. Total discovery is capped at 250,000 workspaces; lineage ancestry is capped at 1,000 links. Oversized data needs a reviewed adjustment or separate migration, never an unbounded run. -- Apply takes `SHARE ROW EXCLUSIVE ... NOWAIT` locks on workspace, Project and membership tables before rediscovering the family. This excludes concurrent inserts/deletes/updates for the short transaction, including new descendants. Reads remain available. Busy writers cause immediate contention rather than an indefinitely queued lock. This is a maintenance operation: ordinary workspace writes can briefly wait while a batch holds its locks. -- A SHA-256 fingerprint covers the ordered environment records, including names, owners, organization, archive timestamps and fork connections. Any change since dry-run rejects that family. Existing Project assignments must match the planned Project ID. Existing names are retained. -- Reads retry transient connection/unavailability failures with bounded jitter. Writes retry only SQLSTATE `40001` (serialization), `40P01` (deadlock) and `55P03` (lock unavailable), at most three attempts. No connection-error write retry: the commit result may be unknown. Constraint errors, cancellation, statement timeouts and unknown errors stop the run. -- Lock timeout is 2 seconds; statements and transactions are capped at 30 seconds. PostgreSQL 17+ is required for the transaction timeout. Runtime is bounded between batches (default 10 minutes, maximum one hour), so the last transaction may finish after the budget. SIGINT/SIGTERM stop after the current transaction; forced termination rolls back an open transaction. -- Reports are replaced atomically before any write and after every committed family. Counts reflect acknowledged commits only. If the connection disappears at commit, counts can underreport; reconcile through the original plan and verification. A failed report write stops further batches; the last durable report remains available. Do not infer rollback from a missing final report. -- Reports distinguish planned counts in dry-run/verify from committed counts in apply. `completed` means the selected scan/plan was processed; `ready` is set only by successful verification. SQLSTATE is recorded without dumping database error payloads or credentials. - -## Conflict repair - -Report entries include the root/environment IDs, relevant Project IDs and reason. Examples: - -- Multiple Projects in one connected family: inspect intended lineage and retained data; repair membership or disconnect the intended subtree transactionally. Never automatically merge Projects. -- Cross-organization lineage: resolve the workspace ownership/lineage first using the approved organization lifecycle operation. -- Changed family after dry-run: inspect the change, then produce and review a new plan. -- Empty Project or archive mismatch: determine the intended retained environments and lifecycle state. Do not silently delete Project data or unarchive environments. -- Cycle or missing parent: repair the fork connection before migration. - -The runner does not repair anomalies on the operator's behalf. After repair, run dry-run and verify again. Membership uniqueness and foreign keys remain active throughout. - -## Self-hosted execution - -Use the migration image from the same release as the application, after all application/worker replicas run compatible code. PostgreSQL 17+ and a direct session-capable primary connection are required. Supply `PROJECT_BACKFILL_DATABASE_URL` through your deployment's secret mechanism; do not place it in shell history or command arguments. Mount a writable, persistent report directory owned by the container's non-root user. - -Inside that image, the working directory is `/app/packages/db`: - -```sh -bun --no-env-file scripts/backfill-projects.ts identity -PROJECT_BACKFILL_REPORT_PATH=/reports/plan.json \ - bun --no-env-file scripts/backfill-projects.ts dry-run -PROJECT_BACKFILL_REPORT_PATH=/reports/apply.json \ - bun --no-env-file scripts/backfill-projects.ts apply \ - --from-file /reports/plan.json --database-id --writers-drained -PROJECT_BACKFILL_REPORT_PATH=/reports/verify.json \ - bun --no-env-file scripts/backfill-projects.ts verify -``` - -Override the migration container command for this one-off operation; do not change the ordinary `db:migrate` startup command. Apply the same report review, writer coordination, replay and activation rules above. Keep the original plan on persistent storage until verification and the release are complete. - -## Local proof - -`packages/db/scripts/project-backfill.integration.ts` creates a separate disposable database for every case, seeds a pre-Project fixture and applies the actual `0393` SQL. It exercises real SQL and the CLI, including interruption, conflicting writers, lost sessions, report failures and idempotent replay. The normal integration workflow discovers this file and uploads the integration JSON result. Never aim this test at an existing app database; the shared test-infrastructure validator restricts the target to a local test database. diff --git a/docs/plans/project-entity-foundation.md b/docs/plans/project-entity-foundation.md deleted file mode 100644 index f2442f8fb8b..00000000000 --- a/docs/plans/project-entity-foundation.md +++ /dev/null @@ -1,320 +0,0 @@ -# Project entity foundation: implementation and rollout proposal - -Backend foundation implemented locally, with backfill hardening validated on 2026-10-01, following the approved planning review. Final feature review and hosted rollout remain separate. Database validation uses disposable test containers only. See the implementation and rollout notes below for the concrete scope. - -Migration `0393_project_foundation` follows `0392_dashboard`. The schema expansion and application writers belong in the foundation release; backfill and contract enforcement follow separately. - - -## Decisions and blockers - -Settled: Project and environment are separate identities; existing workspace IDs remain environment IDs. Issues belong to Project across environments. Project and environment names are independent. Workflow, table, file, knowledge and dashboard data remain environment scoped. Chat has no Project ownership or membership relation. Selecting a Project is navigation state, not execution authority. Ship backend/feature PRs before new UI. - -The user settled Project visibility and Issue access: any environment access makes the Project visible; the toggle lists only accessible environments. Issues are project-wide and all-or-nothing, available by default to anyone with access to any environment. Through permission groups, an org admin can disable all Issues for a Project for partial-access teammates (those lacking access to every environment in that Project). This replaces the earlier explicit Project-membership proposal for Issue access. - -1. **Access — settled:** derive navigation and default Issue access from current environment access. Apply the permission-group restriction only to partial-access teammates, for the target Project; no per-Issue or per-environment filtering of the Issue collection. Restricting Issues does not hide the Project or its accessible environments. Project rename/archive is allowed for org admins and workspace admins who administer every environment; Issue visibility alone does not grant administration. The partial-access Issue check excludes archived environments. -2. **Disconnect — settled:** disconnecting a fork moves it and all descendants into a newly created Project, atomically with unlinking the fork edge. The original Project retains its ID. Existing Issues stay in the original Project; the new Project starts empty. A user may explicitly ask Sim Chat/Mothership to migrate Issues later through an authorized operation, rather than coupling Issue migration to disconnect. -3. **Personal scope — settled:** Projects can exist without an organization. Organization deletion preserves each Project and all of its Issues together, retaining the Project ID and assigning an explicit personal owner; no Project/Issue cascade or replacement Project creation. - -Settled organization-deletion behavior: transfer each Project to an explicitly selected personal owner while retaining its ID and Issues, rather than splitting its Issues between former environment owners. Project archive cascades to every environment and workflow. An active Project must retain at least one active environment; users cannot individually delete/archive the last one. Whole-Project archive is the explicit exception, leaving an archived Project with archived environments, not an empty active Project. Cross-organization environment moves must explicitly separate the moved environment from the old Project or move the entire Project. Existing Issues stay with their original Project on fork disconnect; a later user-requested migration is a separate operation. These policies are particularly important for subscription cancellation and administrative organization deletion. - -No Project member table or grant backfill is required for the settled Issue-access model. Evaluate existing effective environment access and applicable permission-group policy at authorization time. Permission changes immediately affect access; do not materialize permanent Project grants. The Issue partial-access comparison uses non-archived environments, as confirmed by the user. Zero-active-environment Projects are permitted only in archived state, with their environment rows retained. Require at least one accessible active environment for ordinary active-Project access; use explicit archived-resource authorization for archive inspection/restore, never the empty all-environments predicate; do not treat an empty set as full access. External collaborators use their existing effective access, without introducing a new org-membership requirement solely for Issues. - - -Lifecycle behavior: existing Issues remain with the original Project after disconnect; explicit migration can be requested through Sim Chat/Mothership. Org admins or callers with admin on every environment may rename/archive. Archived environments are excluded from the Issue partial-access comparison. The separate all-environment administrative rule includes archived environments; the exclusion applies specifically to Issue access. - -Organization deletion — settled: preserve each Project and its Issues together and assign an explicit personal owner, matching the existing workspace-preservation pattern. Do this atomically with workspace detachment and organization deletion; do not rely on a bare SET NULL FK or infer authority from the billing payer. Detachment assigns the current organization owner as the Project lifecycle owner, matching workspace detachment. If the organization has no owner, the Project retains its required existing lifecycle owner. The FK prevents unresolved user ownership. Subscription lapse is non-destructive as well; it must never reuse a Project/Issue purge path. - -## Organization-deletion preservation contract - -Verified at this planning baseline: - -- `apps/sim/app/api/v1/admin/organizations/[id]/route.ts:302`: transaction calls detach, organization-resource cleanup enqueue, then org delete. Its deletion contract explicitly says workspaces survive; subscriptions referencing the org block deletion. -- `apps/sim/lib/workspaces/organization-workspaces.ts:426`: detach selects organization-mode workspaces without excluding archived rows, clears organization via payer transfer, sets mode to `grandfathered_shared`, and grants admin to the new billing account (org owner if present, otherwise workspace owner). This is not a workspace purge. -- `apps/sim/lib/organizations/resource-cleanup.ts`: captures directly organization-owned files/chats before FK cascade in a transactional outbox. It is not a blanket workspace-resource cascade. -- `apps/sim/lib/auth/auth.ts:1773`: Better Auth organization deletion is disabled. -- `apps/sim/lib/billing/webhooks/subscription.ts:140`: subscription dormancy calls workspace detach, retaining the org. Reusing this helper must not accidentally delete Projects/Issues when a subscription lapses. - -Enumerate affected Projects under the organization lifecycle lock before changing workspace organization/membership. Compare `project.organizationId` with canonical membership/workspace ownership, report inconsistent or mixed-tenant Projects, and include archived org Projects and report legacy empty-Project anomalies that a workspace-only join would miss. Resolve and validate an explicit personal owner for each Project, update ownership to personal, and detach its environments in the same transaction before the organization is deleted. Preserve Project IDs, membership rows, Issues and Issue attachments; do not enqueue them for org-resource cleanup. Existing direct org chat/file cleanup remains separate. Retire organization-specific permission-group bindings explicitly after scope transfer; personal access then uses current effective environment access under the agreed policy. Do not copy grants or restrictions from unrelated orgs. A transfer must not leave a surviving Project referencing a deleted group or organization. Record old/new ownership and audit the actual operator, not the new owner or payer. Restrictive ownership FKs force the lifecycle operation to complete explicitly; they are not a reason to purge data. - -## Column-by-column schema proposal - -The preferred schema below uses environment-derived access and supports the accepted personal Project model. All new identifiers use `generateId()` and text columns, matching workspace and organization IDs. IDs are immutable and independent of names, lineage roots and deployment environment. - -| Column | Proposal and reason | -| --- | --- | -| `project.id` | Text PK, application-generated UUID v4. Never reuse a workspace ID; never recompute after backfill. | -| `project.name` | Non-null text; trim, require 1–100 characters in domain validation and enforce compatible DB bounds/nonblank check. Allow duplicate names; IDs disambiguate. No slug or case-insensitive unique-name constraint without a product requirement. | -| `project.organizationId` | Nullable FK to organization, `ON DELETE RESTRICT`. Null only for explicitly personally owned Projects. An explicit lifecycle transaction transfers Projects before organization deletion. Organization transfer must include archived Projects and surface legacy empty-Project anomalies too. | -| `project.ownerId` (new) | Required user FK, `ON DELETE RESTRICT`, retained on both personal and organization Projects, matching the workspace lifecycle-owner model. Lifecycle ownership, not implicit access. Anchors lifecycle ownership independently of environment owners; individual removal of the last environment is prohibited. Personal-owner deletion transfers or explicitly deletes the Project before user deletion. | -| `project.archivedAt` | Nullable timestamp; setting it archives the Project and all member environments/workflows through one compound operation. Preserve archived environment rows and Issues. Archived detail remains readable to authorized members; normal lists omit it unless requested; Project/Issue mutations reject except restore or approved cleanup. | -| `project.createdAt` | Non-null timestamp default now, immutable. Backfill records actual creation time; retain existing environment history rather than pretending the Project existed earlier. | -| `project.updatedAt` | Non-null timestamp default now; explicitly set by Project metadata/ownership/archive writes. `defaultNow` alone is not an update trigger. Environment membership changes are audited and update the affected environment timestamp; policy changes update their policy records. | -| root/default environment, icon/color, pipeline position, billing fields | Omit from foundation. None is needed for Issue identity or authority. A default execution environment, if added, must still be independently authorized. | - -Indexes: organization list index on `(organization_id, archived_at, id)`; personal-owner list index on `(owner_id, archived_at, id)`; sort by stable ID cursor initially, avoiding a name-based cursor that changes on rename. Add a name-sort index only if the selected list contract requires it. Foreign-key referencing columns need indexes for ownership/deletion lookup. Do not duplicate a simple organization index without demonstrated need. - -| Environment membership field | Proposal | -| --- | --- | -| `project_workspace.projectId` | Non-null text FK to Project. Prefer `ON DELETE RESTRICT` while environments remain, so deleting a Project cannot silently orphan environments. Changing membership on disconnect is an explicit transaction. | -| `project_workspace.workspaceId` | Non-null workspace FK with cascade on workspace deletion; unique index enforces at most one Project per environment. Use a composite PK `(projectId, workspaceId)` plus unique workspace index; Project-leading PK supports member lookup. | -| `project_workspace.createdAt` | Non-null timestamp default now, recording membership-row creation. On reassignment retain creation time and audit the move; introduce assignment timestamp only if required by an actual consumer. | -| `workspace.forkedFromWorkspaceId` | Keep existing lineage edge and `ON DELETE SET NULL`. Explicit disconnect creates a new Project; implicit edge removal on hard parent deletion does not silently move Issue ownership. | -| `workspace.organizationId` | Retain for compatibility/resource authorization. Proposed same-organization invariant with Project uses null-safe comparison and shared transactional scope changes; deferred constraint triggers validate final state across Project, membership and workspace writes. | - -**Membership storage:** a direct non-null `workspace.projectId` would enforce exactly-one membership more simply, but the accepted design uses `project_workspace`; do not implement both. The unique workspace index only enforces at most one. Add deferred constraint triggers on workspace insert and membership deletion/update to require one membership for every surviving workspace at commit after rollout. Lock/recheck the workspace when reassignment can race; deletion must allow cascaded removal for a workspace that no longer exists. Ship this enforcement only after compatible writers and backfill are complete. Require at least one environment membership for each Project and at least one active environment for each active Project. Archived Projects retain member rows but may have no active environments. Complement application checks with deferred final-state constraints after rollout; source and destination Projects must be checked on membership moves. - -Do not add `project_member` for navigation or Issues. Extend the existing permission-group model with a Project-targeted restriction on Issue access for partial-access teammates. Its absence means no extra restriction. Resolve the Project organization canonically; only its org administrators may configure it. Reuse existing group assignment and policy-composition rules instead of inventing a per-user grant table. The current resolver in `lib/permission-groups/config-scope.server.ts` is workspace-keyed; add intentional Project scope rather than passing a selected/root environment. Exact storage follows the permission-group skill during implementation. Project rename/archive authority is org admin OR admin on every environment; do not infer admin from an arbitrary environment. Never grant administration through an empty all-environments check. Active empty Projects are invalid; archived-resource operations authorize explicitly against retained environment grants and org administration. - -## Membership and lifecycle invariants - -1. Every environment, including archived ones, belongs to exactly one Project after enforcement. Each Project contains at least one environment; active Projects have at least one active environment. Whole-Project archive permits zero active environments while retaining archived member rows. -2. Environment and Project organization scopes agree. Personal environments within a Project may have different lifecycle owners only if one explicitly selected Project owner governs Project lifecycle; that must not transfer environment ownership or grant access. -3. Fork creation inherits its parent's Project in the same transaction after rechecking current source scope under lock. Existing fork families seed Projects initially. Explicit disconnection splits the subtree into a new Project. Multiple roots can still arise through parent deletion; do not infer Issue movement from root counts alone. -4. Nested forks inherit the same Project; siblings need not have a unique depth. A disconnected fork and every descendant move together to a fresh Project. Never manufacture a new Project from a missing-parent/backfill race. Reevaluate Issue eligibility on both Projects after the split: a previously partial-access user may become full-access. Project-targeted permission-group restrictions also need explicit migration semantics; proposed preserve applicable restrictions on the new Project rather than accidentally resetting configured policy, while acknowledging the all-or-nothing rule now applies to each new environment set. Record policy handling and both Project IDs in the operation audit. Existing Issues stay with the original Project. Explicit user-requested migration through Sim Chat/Mothership must authorize source and destination Projects under one semantic application operation, with atomic/retry-safe movement and audit; it is an Issues feature, not a Project-foundation dependency. -5. Renaming an environment does not rename the Project; rename the Project through its own operation. Backfill and implicit Project creation through legacy workspace creation use ` - Project`; explicit Project creation accepts independent Project and initial environment names. Workspace names remain untouched. Bound the generated Project name without losing its suffix, with `Untitled` for an empty source; surface any length normalization in the report. Duplicates are allowed and recorded, not merged. -6. Individual environment delete/archive must leave at least one active environment in an active Project and at least one member environment overall. Block removal of the last environment; direct hard-delete, account deletion, moves and fork disconnect cannot bypass the invariant. Serialize on the Project so two concurrent removals cannot each see the other as the remaining environment. For disconnect/move, validate both resulting Projects; refuse a split that strands the original as active with no active environment. Coordinated whole-Project lifecycle operations are the explicit exception for active-count checks, not an adapter flag that callers can freely set. -7. Project archive is one authorized compound operation that archives every environment and workflow, including operational deactivation already owned by existing archive paths. Retain membership rows and Issues. Reject new environment/fork/workflow admission into an archived Project. Preserve pre-existing archived states. Current `lib/workspaces/lifecycle.ts` commits workspace/resource archival before calling `archiveWorkflowsForWorkspace`; looping this helper across environments would permit a partially archived Project. Refactor transaction-owning primitives so durable Project/environment/workflow state changes commit atomically, with existing required external effects queued/retried after commit. Failure must not be reported as complete while workflows remain active. Project restore is deferred until a complete inverse exists; do not ship a metadata-only unarchive that leaves no active environments or silently revives resources archived before the Project. Archival does not imply cancellation of already-running executions beyond existing archive semantics unless separately specified. -8. Moving one environment between organizations cannot retain its old Project membership. For initial shipment, refuse a partial Project transfer unless the operation explicitly names a destination/new Project and defines the Issue outcome; preserve original Project Issues. A whole-Project transfer requires authorization and disclosure for all environments, retained Project data, collaborators and Issues. -9. Organization cancellation/detach/deletion must process Projects and all member environments atomically with existing payer transfers. The legacy per-owner workspace detach cannot silently determine how to split Project-wide data. If a complete, authorized transfer cannot be determined, stop before committing rather than silently orphaning or broadening access. -10. Project hard delete is not required to unblock Issues. Restrict it while environments or Issues exist. `issue.projectId` should be NOT NULL with restrictive deletion until an explicit purge policy is implemented. Deleting an account must account for personally owned Projects and retained Issues, not merely surviving workspace grants. - -## Authorization and application surface - -Provide a shared canonical Project application context: `{ projectId, organizationId, ownerId, archivedAt }`, loaded by ID, with optional asserted organization/workspace scope compared to canonical data. Return not-found for hidden/mismatched scope, consistent with shared concealment policy. Loading must be inside the authorized application-use-case lifecycle; an HTTP/tool adapter may not query protected membership itself. - -Create a small shared Project authorization wrapper alongside the existing workspace/organization wrappers. It must reject unsupported Principal kinds before loading protected data; authorize current effective environment access and applicable Project-scoped permission-group policy; distinguish Issue use from Project administration; respect archive state and typed errors; project semantic audit from authoritative results; and keep external side effects after success. Mutations lock and recheck Project state/authority sufficiently to serialize with environment access revocation, environment-set changes, policy changes, organization transfer and archive. Persist a durable audit/outbox record inside the transaction where existing shared infrastructure supports it; do not report a committed mutation as absent because a later notification fails. - -Do not use `defineAuthorizedWorkspaceUseCase` with an arbitrary root environment. Staging has both `defineWorkspaceOperation` and `defineOrganizationOperation`; the latter currently accepts session/organization-delegated principals and cannot model personal Project scope alone. Extend the shared foundation intentionally, rather than hiding authorization in `projects.ts` or borrowing a payer identity. - -Proposed first contract (session surface; supported principal expansion is explicit): - -| Operation | Authority and behavior | -| --- | --- | -| `projects.list` | Paginated Projects containing an accessible environment, in requested owner scope; archived Projects require a separate explicitly authorized archive listing, never vacuous environment-access checks; archived filter. No inaccessible environment IDs, names, counts or fork parent references. | -| `projects.get` | Basic navigation metadata visible through an accessible environment (archived-resource access uses a separate explicit policy); Issue and administrative capabilities checked separately; stable `{ id, name, organizationId, archivedAt, createdAt, updatedAt }` plus caller capabilities. | -| `projects.resolveForWorkspace` | Authorize environment access; it permits resolving basic Project navigation metadata through the canonical `project_workspace` membership, without requiring a separate Project grant. Never accept a supplied Project ID as truth. | -| `projects.listEnvironments` | Project navigation visibility plus each environment's existing access policy; filter hidden parents in lineage output. Paginate rather than embedding all environments in every Project result. | -| `projects.create`, `rename`, `archive` | Creation atomically creates the first environment under existing creation/quota policy. Rename/archive require org admin or admin on every environment; archive cascades to all member environments/workflows. Do not automatically add member grants. No zero-environment creation endpoint. Restore is deferred until full environment/workflow lifecycle behavior is defined. | -| `projects.addEnvironment` | One compound use case authorizing Project administration and existing workspace-creation policy, then atomically creating environment with correct Project membership. Legacy workspace creation still creates its own Project. | -| Project Issue restriction configuration | Existing permission-group application operations extended with Project scope; org-admin authorization and canonical group/Project organization matching. No separate Project membership CRUD. | -| Issue operations | Use the all-or-nothing Issue gate below; action-specific mutation policy remains to be agreed without introducing partial Issue visibility; canonical Issue lookup verifies `issue.projectId`. No environment ID required for Issue CRUD. Links to environment resources are individually authorized on resolution. | - -Issue authorization for a human caller uses the complete canonical environment set E and the subset A for which the caller currently has effective access. The server must compute E independently of the filtered environment-toggle response: - -```text -canViewProject = |A| > 0 -hasPartialEnvironmentAccess = |A| > 0 AND |A| < |E| -canUseProjectIssues = canViewProject - AND NOT (hasPartialEnvironmentAccess AND effectiveGroupPolicyDisablesIssuesForThisProject) -``` - -The agreed truth table is: no accessible environment -> no default Issue access; some environments -> all Issues by default, none when the restriction applies; all environments -> the partial-access restriction does not deny Issues. Normal authentication, principal-kind and archive/action rules still apply. Return Issue capability separately from navigation capability. Never return hidden environment names/IDs/counts merely to explain a denial. - -Enforce this once in the shared Project Issue authorization path used by list/detail/mutations, search, counts, notifications/subscriptions, exports and tools. A denied caller must not receive Issue titles or other Issue data through side channels. Existing environment resources linked from Issues still require their own environment checks; access to all Issues never grants production access. Ordinary Issue mutation roles belong to the Issues implementation, but may not create a per-environment Issue subset. Project rename/archive uses org-admin OR all-environment-admin authority; permission-group edits remain org-admin operations. - -Creation/deletion/archive/restoration/move of environments and access grants/revocations can change partial/full status. Reevaluate on the server and invalidate relevant capability/policy caches and live subscriptions. Serialize mutating checks with changes that can alter the decision. Do not select the policy using whichever environment the user toggled to. Missing policy defaults to allow, but a policy lookup failure must propagate as an error, not be interpreted as an absent restriction. Archived environments are excluded from this Issue access comparison, as confirmed by the user. - -Start Project HTTP routes at `/api/projects` and `/api/projects/[id]`, with named contracts under `lib/api/contracts/projects.ts` and shared builders. Add environment lookup and permission-group configuration routes as needed by the first consumer. Query hooks use `requestJson`, named contract types and React Query. Start with a session-only Project API; do not advertise unsupported key/delegation support. - -Workspace API keys and workspace-scoped executor/system principals **must not** gain project-wide Issue access just because their environment points at the Project. Personal API keys/OAuth need an explicit Project credential policy (there is no unambiguous environment `allowPersonalApiKeys` switch at this scope), scope enforcement and permission-group checks before v2 Project routes ship. Organization delegation already exists, but is documented/search-scoped; extend trusted audience/operation policies and recheck current subject membership before admitting Project operations. Org chat Project selection is a requested target only. No chat-to-Project relation, implicit default environment, or copied grant. - -The Issues implementation can build on the foundation contract. Existing environments become usable for Issues only after validated backfill/enforcement; new properly provisioned Projects can be exercised earlier behind a release gate. No promise of a production-ready ID resolver that invents IDs on reads. - -## Required code-path inventory - -Paths below are relative to the repository root. `Migrate` means membership/authorization/lifecycle behavior is in scope; it does not mean copying prototype implementations wholesale. - -| Path / ingress | Classification and required work | -| --- | --- | -| `packages/db/schema.ts`, `migrations/`, `migrations/meta/_journal.json` | Migrate: additive Project/environment-membership schema and Project-scoped permission-group restriction, later constraints; preserve staging's dashboard migration. | -| `packages/db/scripts/migrate.ts`, `script-migrations/index.ts`, `script-migrations/types.ts` | Migrate integration: SQL runs before registered scripts under a session migration lock. This lock does not stop application writers. Do not put a large unbounded backfill in startup. | -| `apps/sim/lib/workspaces/create.ts` | Migrate both `createWorkspaceInTransaction` and wrapper; Project, membership and workspace created atomically. Its `createDefaultPersonalWorkspaceInTransaction` covers enterprise owner-claim provisioning. | -| `apps/sim/app/api/workspaces/route.ts` | Migrate normal POST and lazy/default workspace creation through a shared authorized application creation operation; preserve legacy wire behavior and existing workspace URLs. | -| `apps/sim/lib/workspaces/application/create-organization-workspace.ts`; `lib/mothership/application/execute-organization-workspace-use-case.ts`; `lib/mothership/tools/server/workspaces.ts` | Migrate shared lower creation path; keep Mothership a trusted adapter and preserve org pinning, capabilities, billing admission and resource refresh behavior. | -| `apps/sim/lib/billing/enterprise-owner-claim.ts` | Migrate inherited transaction behavior; no nested independently committing Project creation. | -| `apps/sim/ee/workspace-forking/lib/create-fork.ts`; `application/`; `lib/lineage/{lineage,lineage-root,unlink}.ts` | Migrate Project inheritance/validation under existing rank-ordered lineage and row locks. Disconnect atomically creates a new Project and reassigns the complete descendant subtree. Repeated/concurrent unlink must not create duplicate Projects; preserve existing edge-lock ordering and reread the closure. Do not add an organization lock held across the entire copy without reconciling lock order. | -| `apps/sim/app/api/workspaces/[id]/route.ts`; `lib/workspaces/lifecycle.ts` | Migrate required use-case boundaries; environment rename remains independent, DELETE is archive; guard last-active-environment removal. Add Project-wide atomic archive primitives while preserving existing resource archival/deactivation semantics and retryable effects. | -| `apps/sim/lib/workflows/lifecycle.ts` | Account-related bulk workspace archive must obey Project last-environment invariants; full Project archival must enlist workflow state in the same compound operation. Transfer surviving Projects on account deletion or use an explicit whole-Project lifecycle path. Workflow restore is not workspace restore. | -| `apps/sim/lib/workspaces/organization-workspaces.ts` | Migrate attach/detach closure, locks, Project scope and personal-owner transfer together with existing payer changes; include archived environments/Projects and report legacy empty-Project anomalies. | -| `apps/sim/lib/invitations/core.ts`; `lib/billing/organization.ts`; `app/api/v1/admin/organizations/[id]/members/route.ts`; `scripts/consolidate-users-into-organization.ts` | Required attach callers. Reuse corrected transaction primitive; recompute Project visibility/Issue eligibility after environment grants or scope changes. Do not broadly refactor v1 APIs. | -| `apps/sim/lib/billing/webhooks/subscription.ts`; `app/api/v1/admin/organizations/[id]/route.ts` | Required detach/cancellation/delete callers; explicit Project lifecycle before restrictive org FK. Preserve atomic detach+delete and billing semantics. | -| `apps/sim/lib/workspaces/admin-move.ts`; `admin-move-source-impact.ts`; `lib/admin/member-operation.ts`; `lib/billing/enterprise-provisioning.ts`; `app/api/v1/admin/dashboard/workspaces/[id]/move/route.ts` | Migrate transfer preflight/disclosure, Project conflicts and transaction ownership. Existing moves can dissolve fork edges; that alone cannot define Project transfer. Keep admin operation receipts and recoverable audit. | -| `apps/sim/lib/billing/organizations/membership.ts` | Migrate member removal, external removal, member transfer and organization-owner transfer to invalidate Project Issue eligibility and handle personal lifecycle ownership alongside workspace permission rows. | -| `apps/sim/lib/users/account-deletion.ts` | Migrate deletion planning/rechecks/transaction for retained Issue data, owned Projects and last-environment guards, transfers and Project FK restrictions. Direct workspace deletion uses alias `workspaceTable`; a search only for `delete(workspace)` misses it. | -| `apps/sim/lib/workspaces/{list,public-queries,host-context,seed-workspace-list}.ts`; `application/list-*.ts`; `packages/platform-authz/` | Preserve existing environment authority. New Project reads must not replace resource scope or turn Project membership into environment permission. Add owner-context resolution only where needed. | -| `apps/sim/lib/workspaces/application/{manage-permissions,remove-member}.ts`; `permissions/`; `access/workspace-access.ts`; invitations and organization member operations | Required access inventory. Reevaluate any/all environment access at invite/removal and update live Issue access; do not materialize permanent Project grants. | -| `apps/sim/app/api/v2/workspaces/**`; `lib/api/contracts/v2/`; `lib/api/mcp/generated/v2-operations.ts`; SDK/CLI generation | Preserve existing environment API. Project APIs are an additive surface after credential policy, using the same Project application operations. Regenerate descriptors only when adding that surface. | -| `apps/sim/app/api/v1/admin/workspaces/[id]/{import,export}/route.ts`; `lib/workflows/operations/import-export.ts`; `lib/workspaces/__integration__/mapped-import.integration.ts` | Reviewed scope: import works into an existing environment, not a new Project. Preserve identity and never import Project grants/IDs from resource payloads. No broad v1 migration. | -| `apps/sim/background/cleanup-soft-deletes.ts`; `lib/billing/storage/{payer-transfer,tracking}.ts`; `lib/mothership/inbox/{lifecycle,settings-store}.ts` | Reviewed adjacent writers: resource cleanup, payer/accounting and inbox settings are not Project identity changes. Project archive must invoke established environment/workflow deactivation; preserve existing retention policy and do not add immediate hard purge. No blanket edits to every workspace update. | -| `apps/sim/lib/permission-groups/{config-scope.server,resolve.server,fields,mutation}.ts`; `application/`; group contracts/settings UI | Extend Project-scoped partial-access Issue restriction through existing authorized group operations. Default absent setting to allow; preserve other fields on update; resolve all applicable groups using existing composition rules. Validate group/Project organization and invalidate Project Issue capabilities on edits. Read `add-permission-group-item` before implementation. | -| New `apps/sim/lib/projects/application/**`, contracts, internal routes, hooks | Implement fresh against staging shared foundations. Session operations first; add other adapters only with intentional Principal policy. | -| Prototype `lib/mothership/chat/lifecycle.ts`, `chat/application/fork.ts`, `chat/list-mothership-chats.ts` and `copilot_chat_project` | Non-goal: exclude chat relation imports, writes, filters, backfill and schema. Separate chat-ownership work removes obsolete prototype relations if ever deployed there. | -| Prototype `app/o/[organizationId]/p/hooks/use-projects.ts` and resource-browser/UI port | Defer: consume true Project IDs only after foundation. Extract final behavior later; no prototype cherry-picks. | - -Implementation must repeat the write-ingress search after rebasing (including schema aliases/raw SQL, user cascades, scripts and fixture writers). This is a source-based inventory, not a claim that production rows already satisfy the invariants. - -## Backfill and deployment procedure - -### Expand and capture a plan - -1. Ship additive Project and membership tables without enforcing membership completeness yet; keep current UI/API functional. Do not rewrite workspace names, routes, resource IDs or chat rows. Ship compatible writers in the same foundation release, but account for old replicas during rollout. Backfill activation and final constraints are separate deployment steps. -2. Use `packages/db/scripts/backfill-projects.ts`, with explicit `PROJECT_BACKFILL_DATABASE_URL`, `PROJECT_BACKFILL_REPORT_PATH`, and `dry-run` / `apply --from-file --database-id --writers-drained`. Run with `bun --no-env-file` so a checkout’s `.env` cannot select the database. The operator must verify the supplied target; there is no implicit application-DSN fallback. -3. Scan all workspaces, including archived, in stable ID keyset pages. Discover graph components with cycle detection and bounded traversal; roots are rows with no parent, missing parents are reported, cycles and organization mismatches are blocking anomalies. Keep only one bounded family in memory. Committed Project and membership rows serve as the durable resume mapping. Never silently reconnect already detached lineages or group unrelated roots by equal name. -4. Group existing environments by their current fork connections. A root workspace and all forks still connected beneath it become one Project. For example, Production → Staging → Dev becomes one Project containing all three environments. If Staging was previously disconnected from Production, create two Projects: one for Production, and one for Staging plus Dev. A workspace with no connected forks gets its own Project. Each new Project uses the root workspace’s name plus “ - Project” and its `ownerId`. - - Preserve any valid Project assignment that already exists. If only some environments in the tree have been assigned to one compatible Project, add the remaining environments to that same Project. If the tree is assigned to multiple Projects or spans multiple organizations, report the conflict and leave it unchanged for review; do not automatically merge it. - - If all environments in the tree are archived, create an archived Project and use the most recent environment archive timestamp. Otherwise, create an active Project. Never reactivate an environment during migration. An existing Project with no environments is reported for repair because every Project must contain at least one environment. - -5. Allocate each proposed Project ID in the completed dry-run artifact with `generateId()`. Apply consumes that artifact and validates its database identity and family fingerprints. Reruns use the same planned IDs, including after an interrupted apply; compatible existing assignments are reused. No extra manifest table is required. -6. Duplicate names are allowed; bound and trim seed names deterministically and record before/after. An ID collision fails the batch; never silently replace a planned ID during apply. Never overwrite an unrelated Project on collision. No Project-member/grant backfill; existing groups gain an absent/default-allow restriction without changing existing settings. No chat backfill, no Issue backfill in this PR. - -### Coordinate writers and commit batches - -7. Before apply, drain old app/worker/script writers or use a bounded write-maintenance window for the affected lifecycle operations. The migration runner lock serializes migration runners only. A one-shot script registered in the startup runner may finish before old replicas stop creating unassigned environments and is not a readiness signal. -8. New create/fork/attach/detach/move writers and the backfill must share a documented lock order. Integrate with existing organization, invitation, payer, lineage and workspace locks; do not introduce an opposite Project→organization order. Lock scope owners/Project/family according to that rank plan, then member rows in stable code-unit ID order, re-read closure and fingerprints under lock, and retry if scope changed. For fork versus bulk attach/detach, the closure must be re-read after serialization so a newly committed child cannot be omitted. If online closure coordination cannot be proved, use the maintenance window for those operations. -9. Each normal family commits Project + all environment assignments atomically. Insert membership only for unassigned workspaces; a unique-workspace conflict requires a locked reread and validation of the expected Project, not silent success. Do not ignore unique conflicts. Keep counters/report progress after commit; counters must describe committed work. -10. Bound batches by rows and transaction duration, not only 200 roots. Oversized families are explicitly reported and processed in a dedicated controlled transaction/window, or through a staged assignment design whose incomplete state is hidden from Project consumers. Never expose a partially migrated Project as complete. Resume from the original dry-run plan and committed memberships; recover committed batches after process death without new IDs or accidental policy restrictions. -11. Keep legacy workspace UI operational while Project/Issues reads are release-gated until readiness. During rolling deployment a missing membership is an explicit not-ready state, not permission to create a new Project on a GET. A compatible fork writer must ensure its parent family transactionally, or temporarily refuse fork creation for an unassigned family. - -### Validate and enforce - -The command must emit counts and offending IDs for at least these checks (illustrative SQL for the proposed schema): - -```sql --- Must be zero before exactly-one enforcement. -SELECT w.id FROM workspace w -LEFT JOIN project_workspace pw ON pw.workspace_id = w.id -WHERE pw.workspace_id IS NULL; - --- Must be zero; use null-safe comparison for personal scope. -SELECT w.id, pw.project_id FROM workspace w -JOIN project_workspace pw ON pw.workspace_id = w.id -JOIN project p ON p.id = pw.project_id -WHERE w.organization_id IS DISTINCT FROM p.organization_id; - --- Existing fork edges must stay in the same Project. -SELECT child.id FROM workspace child -JOIN project_workspace child_pw ON child_pw.workspace_id = child.id -JOIN project_workspace parent_pw ON parent_pw.workspace_id = child.forked_from_workspace_id -WHERE child_pw.project_id IS DISTINCT FROM parent_pw.project_id; - --- No active Project may lack active environments. -SELECT p.id FROM project p -WHERE p.archived_at IS NULL AND NOT EXISTS ( - SELECT 1 FROM project_workspace pw JOIN workspace w ON w.id = pw.workspace_id - WHERE pw.project_id = p.id AND w.archived_at IS NULL -); - --- Even archived Projects retain at least one environment row. -SELECT p.id FROM project p WHERE NOT EXISTS ( - SELECT 1 FROM project_workspace pw WHERE pw.project_id = p.id -); - --- Every Project retains a user lifecycle owner. -SELECT id FROM project -WHERE owner_id IS NULL; - -``` - -Additionally validate FK orphans, cycles/missing parents, duplicate/partial manifest assignment, permission-group Project/organization mismatches, counts of archived environments, empty Projects, source permission changes since planning, and no Project membership implied by chat data. Empty Projects and active Projects with no active environments are blocking anomalies; repair them explicitly, never silently delete retained Issues. Duplicate names and fully archived Projects with retained environments are valid. Check plan/report counts against actual committed rows and ensure every blocked family is resolved before completion. - -12. After compatible writers are fully deployed and validation passes, add/validate constraints, enforce exactly-one membership, and enable consumers. Use staged `NOT VALID` validation for existing-row constraints where appropriate; lock-time-bounded DDL and staging's documented concurrent-index mechanism for large workspace indexes. A cross-table deferred trigger must validate both workspace and Project ownership updates and serialize conflicting writes; test concurrent transactions, not just final-state SQL. Ordinary FKs still own referential existence. -13. Repeat validation after a normal write interval. Confirm a fresh install and an upgraded self-hosted database reach the same ready state. An operator-run production backfill needs a documented self-hosted upgrade path; readiness must remain false until it completes. Do not silently hang all deploys on a production-sized graph scan. - -### Recovery and rollback - -Before enforcement/consumer activation, application rollback may ignore additive schema, but old writers require pausing the backfill and another reconciliation before reactivation. After exactly-one membership enforcement, rolling back to old writers is unsafe: they cannot create environments. Roll back only to the compatible foundation release, or deliberately relax the constraint with writes gated and rerun reconciliation afterward. - -Keep IDs, membership rows, and operator reports durable. If a backfilled grouping is wrong before Issues begins, correct it transactionally from the recorded mapping with an explicit reviewed repair. Once Issues reference a Project, do not undo by dropping Projects or regenerating IDs; use a forward repair with an explicit Issue retention/move decision. Preserve backups and reports. No automatic destructive down migration. Retry failed batches; committed family assignments survive. A script runner's completed-name marker is not a substitute for a row-level readiness check. - -## PR and dependency sequence - -| PR | Scope and dependency | Deploy boundary | -| --- | --- | --- | -| A — Project foundation | Clean implementation branch from fresh staging; schema expansion, environment-derived Project/Issue access and Project-scoped partial-access restriction, stable IDs and session contracts, shared creation/fork writes, required lifecycle compatibility, dry-run/apply tooling and focused proofs. No chat/UI. | Additive deployment; existing workspace UI works; Project consumer gate initially off. Project/environment placement are atomic. Required org/user lifecycle handling must not be deferred past activation. | -| B — migration enforcement | Depends on A: bounded apply runbook, validated data/readiness, constraints and activation. Can be a small separate PR/release, or a second deployment phase of A. | Must follow compatible-writer rollout and measured validation. No hardcoded success flag standing in for data integrity. | -| C — Issues | Issues stacks on A after access/lifecycle contract is agreed; merges against A and activates after B. Issue FK, application operations, routes and any feature UI independently reviewable; enforce the partial-access restriction across every Issue surface before enabling it. | Issue identity is Project ID; no environment-owner authorization fallback. | -| D — chat ownership | Independent of A/B/C. Org/personal chat ownership and resource browsing, with obsolete prototype Project references excluded. | No chat-to-Project relation or Project migration dependency. | -| E — remaining backend features | Resource-browser APIs, environment management and other feature foundations as separate PRs. Dashboards already exist in staging; do not reintroduce prototype migration. | Depends on A/B only where actual Project operations are used. | -| F — new organization/Project UI | Built on landed feature surfaces; extract completed behavior from the aggregate experiment. True Project ID and selected environment ID remain distinct. | Last; current workspace navigation remains supported until replacement is deliberately shipped. | - -If A becomes too large, split a prerequisite shared authorization/closure-lock fix from the entity PR, then stack A on it. Do not split out required lifecycle writes such that an enabled Project entity can silently corrupt on cancellation, invitation acceptance or account deletion. A schema-only merge can be additive, but is not the complete foundation required by Issues. - - -## Verification plan and release evidence - -Apply the `test-audit` authoring gate before code: enumerate failure modes, choose one strongest owner boundary, and avoid schema/registry/config assertions. Use disposable Postgres/Redis and an isolated app. Report to caller-supplied `PROJECT_FOUNDATION_REPORT_PATH` / `PROJECT_BACKFILL_REPORT_PATH` with each check's status, duration, failure and IDs; upload on CI failure. - -| Real boundary | Failure to prove | -| --- | --- | -| Migration against pre-Project DB snapshot | Active/archived singleton, siblings, nested forks, detached family, duplicate names, mixed owners, missing parent, cycle, cross-org family, prior partial/split assignments. Valid rows map once; anomalies block without mutation; rerun uses identical IDs and preserves default-allow policy. | -| Real concurrent DB transactions | Fork versus backfill; fork versus attach/detach/move; whole-Project archive versus fork/environment/workflow creation; two concurrent last-environment removals; archive versus Issue write; environment access revoke versus Issue write; full-to-partial transition versus Issue write; restriction edits versus cached reads; crash between batches. Assert committed scope and recoverability, not mocked call order. | -| Real create/provision/fork HTTP or application boundary | Workspace+Project rollback together after injected transactional failure; regular creation, first visit, enterprise claim and org Mothership creation all obey membership. | -| Real Project/Issue HTTP | Staging-only grant shows the Project and only staging in its toggle, with production hidden; partial-access user sees all Issues by default and none with the applicable group restriction; full-access user retains Issues under that restriction; no-environment user gains nothing; restriction changes do not hide accessible environments; list/detail/search/counts/notifications/tools honor denial; cross-org assertion concealed; workspace API key rejected; archived Project read allowed/write denied; active empty Project creation/removal is rejected; archived-state checks cannot grant access through an empty set; revocation effective without stale authorization cache. | -| Lifecycle E2E | Independent renames; disconnect creates one new Project for the complete subtree, retains original Project ID, and leaves existing Issues in the original Project; individual archive/delete of last environment is rejected; whole-Project archive covers every environment/workflow atomically and retains Issues; org cancellation/delete produces approved personal ownership or atomic refusal; user deletion handles retained Issues and last-environment invariants; transfer rollback preserves billing and scope. | -| Current UI compatibility | Existing workspace URLs, resource execution and import/export still work; no chat migration; Project selection cannot choose an execution environment implicitly. | -| Upgrade/install | Fresh install, interrupted backfill resume, rolling old/new writers before enforcement, enforced-release rollback boundary and self-hosted completion path. | - -Extend existing real suites where they own the failure: `lib/workspaces/__integration__/fork-sync.integration.ts`, `http-cli.integration.ts`, `mapped-import.integration.ts`, and `ee/workspace-forking/lib/lineage/fork-lock-order.integration.ts`; read their full coverage before deciding on additions. Project authorization and migration have new independent contracts and need real owner-boundary proofs. Existing type checks cannot prove concurrent ownership integrity. Demonstrate regressions go red with each relevant guard removed; never add a test that only restates a table definition. - -Implementation checks: relevant integration/E2E runs; workspace type-checks (app/db/auth/platform-authz as touched); `bun run check:api-validation:strict`; `bun run check:audits`; repository lint with review of autofixes; `git diff --check`. Local validation now includes 35 foundation/fork/lifecycle integration tests, 9 dedicated backfill/CLI integration cases, app/database type checks, lint, all 57 audits and diff checks. A negative control confirmed the changed-family guard catches a newly added descendant. These checks do not represent full coverage of every future-consumer scenario in the matrix above. No hosted backfill, browser rehearsal or production activation has occurred. - - -### Ownership clarification during implementation - -Every Project retains a required `ownerId`, including organization Projects. `organizationId` is optional; the two fields are not mutually exclusive. This matches workspace lifecycle ownership. Neither field substitutes for explicit workspace grants or organization-admin authority. A departing owner is transferred before account deletion; Project ownership does not cascade away with a user. Organization detachment keeps Project identity and explicitly assigns its personal lifecycle owner. - - -## Concrete implementation and rollout notes - -The schema contains `project` (`id`, `name`, required `ownerId`, nullable `organizationId`, `archivedAt`, `createdAt`, `updatedAt`) and `project_workspace` (`projectId`, unique `workspaceId`, `createdAt`). There is no stored fork depth, position, or Project billing state. - -For request/response examples and the distinction between explicit Project creation and existing workspace creation, see the [Project creation guide](../../apps/sim/lib/projects/README.md). - -Implemented session routes: - -- `POST /api/projects`: creates a named Project and its named first environment atomically, including admin permissions and a starter workflow. Requires explicit `organizationId` (or `null` for personal scope), `name`, and `initialEnvironment.name`; returns both IDs and names with HTTP 201. Uses existing workspace creation eligibility, billing and permission-group rules. -- `GET /api/projects`: authorized, cursor-paginated active Project inventory. -- `GET /api/projects/[id]`: metadata, accessible active environments, and administration/Issues capabilities. -- `GET /api/projects/by-workspace/[workspaceId]`: canonical lookup that also authorizes the requested environment. -- `PATCH /api/projects/[id]`: rename. -- `DELETE /api/projects/[id]`: compound Project/environment/workflow archive. - -Workspace creation creates its Project atomically; fork creation joins the existing Project. Existing workspace creation callers retain their response shape and generated Project names. Project creation shares their transaction primitive; it cannot attach an arbitrary environment or create an empty Project. A separate empty-Project create, arbitrary environment attachment, restore UI, public-key API, and Issues CRUD are not implemented here. The existing permission-group editor gains the Project Issues restriction; the new Project navigation UI remains a separate consumer. - -`deniedPartialAccessProjectIssues` is a Project-ID denylist on existing permission-group config. An empty list allows access. For a partial-access teammate, each accessible active environment resolves its effective group using existing explicit-member/all-members/default precedence. If any of those effective groups denies this Project, all Issues are denied. The selected environment cannot change the answer. Archived environments are excluded from this comparison. Full-access teammates bypass this partial-access restriction. Issue use cases call `authorizeProject` in their own transaction before reading or writing Issue rows; `getProjectIssueAccess` is only a read-only probe. The Issue gate holds the permission-group lock through that transaction, so callers must respect its existing leaf-lock rule. - -Disconnect copies a matching restriction to the new Project. Moving a Project out of an organization removes its stale ID from that organization’s group configs. Rename/archive authorization includes every environment, including archived members. A departing organization member’s Project lifecycle ownership moves to the organization owner. Account teardown transfers surviving Projects to an administrator of their surviving environments and explicitly removes wholly private Projects in the same teardown transaction; it cannot leave a surviving active Project with no active environment. - -The backfill scans 100 workspaces per page, limits ancestor traversal to 1,000 links and CLI family batches to 1,000 environments by default (10,000 maximum), and commits one family at a time. It reports cycles, missing parents, oversized families, cross-organization lineages, conflicting assignments and inconsistent archive states. A reserved session advisory lock excludes overlapping backfill runs. Apply takes bounded NOWAIT table locks before rediscovering and validating the full family. Organization moves use a nonblocking Project lock because their legacy paths can already hold workspace locks; contention returns a retryable conflict. A conflicting batch leaves prior committed families intact and is safe to rerun after repair. - -Operator sequence: - -1. Deploy the additive migration and compatible writers. Do not enable downstream Project/Issues consumers yet. -2. Run dry-run with an explicitly verified database target and inspect its report. -3. Drain old writers and pause lineage/ownership maintenance while applying. Pass `apply --from-file --database-id --writers-drained`; this flag is an operator assertion, not an automatic drain. Legacy unassigned workspaces remain usable, but their fork operation returns a backfill-required conflict until assignment completes. -4. Rerun and verify zero new assignments and zero conflicts, then execute the reconciliation queries in this plan. Resolve any remaining unassigned/empty/mismatched rows before activation. -5. Enable consumers only after reconciliation. Complete database-level membership/minimum-environment enforcement in a later deployment, as recorded by `contract-pending` in the schema. Do not activate consumers on old-writer rollback without another reconciliation. - -Example invocation from the repository root (supply the target URL through your normal secret mechanism): - -```sh -PROJECT_BACKFILL_REPORT_PATH=/absolute/path/project-backfill-dry-run.json \ -bun --no-env-file packages/db/scripts/backfill-projects.ts dry-run - -PROJECT_BACKFILL_REPORT_PATH=/absolute/path/project-backfill-apply.json \ -bun --no-env-file packages/db/scripts/backfill-projects.ts apply \ - --from-file /absolute/path/project-backfill-dry-run.json \ - --database-id --writers-drained -``` - -Validation artifacts come from `PROJECT_FOUNDATION_REPORT_PATH` (or `apps/sim/test-results/project-foundation.json`) and the integration runner’s JSON report. CI already uploads `apps/sim/test-results/*.json`. The former mock-count workspace lifecycle tests were replaced by real database checks of concurrent removal and retry repair. The dedicated backfill suite executes the exact additive migration in a disposable database before exercising backfill. External webhook/socket/MCP cleanup retains existing best-effort post-commit behavior; durable archive state commits atomically. - -The operator-run CLI procedure, self-hosted commands, SQLSTATE handling, lock behavior, report interpretation and repair procedure are documented in [the Project backfill runbook](./project-backfill-runbook.md). - -### Follow-up: Project-scoped files - -Project names provide the identifying label; this foundation intentionally omits a separate description field. Shared purpose, goals, and operating guidance belong in Project-owned files, with a designated brief identified by a stable document ID. - -Extend file ownership to Projects in a separate PR, likely with nullable `projectId` on the existing files table and constraints for valid scope combinations. Include authorization, version history/collaboration, storage lifecycle, and Mothership tools/VFS support. Keep environment files restricted to their environments; publishing them to a Project must explicitly account for broader readership. Project-scoped files and brief consumption are not implemented in this foundation. diff --git a/packages/db/scripts/backfill-projects.ts b/packages/db/scripts/backfill-projects.ts index 1512838d2d5..3dc9775b5e2 100644 --- a/packages/db/scripts/backfill-projects.ts +++ b/packages/db/scripts/backfill-projects.ts @@ -22,7 +22,12 @@ Commands: Required environment: PROJECT_BACKFILL_DATABASE_URL (direct primary connection). Reports: PROJECT_BACKFILL_REPORT_PATH (required except identity). -Apply requires --writers-drained and --database-id FINGERPRINT. +Apply requires --writers-drained --project-writers-enabled --release-revision REVISION +and --database-id FINGERPRINT. These are operator assertions, not fleet verification. +Deploy first with PROJECT_WRITES_ENABLED=false and PROJECT_API_ENABLED=false. +Drain old code, enable Project writers on every compatible instance, then backfill. +Keep the Project API off until verification and later contract enforcement. +Once Project data exists, never roll back to code that predates Project support. Options: --from-file PATH, --seconds 600 (1–3600), --max-family-size 1000 (1–10000). Run with compatible writers deployed. Pause lineage/ownership writes during apply. Short NOWAIT table locks exclude concurrent changes during each family transaction. @@ -38,6 +43,8 @@ async function main() { help: { type: 'boolean' }, 'from-file': { type: 'string' }, 'writers-drained': { type: 'boolean' }, + 'project-writers-enabled': { type: 'boolean' }, + 'release-revision': { type: 'string' }, 'database-id': { type: 'string' }, seconds: { type: 'string', default: '600' }, 'max-family-size': { type: 'string', default: '1000' }, @@ -78,13 +85,34 @@ async function main() { throw new Error('Invalid command') const reportPath = process.env.PROJECT_BACKFILL_REPORT_PATH if (!reportPath) throw new Error('Set PROJECT_BACKFILL_REPORT_PATH explicitly') + const releaseRevision = values['release-revision']?.trim() ?? '' if ( mode === 'apply' && - (!values['writers-drained'] || values['database-id'] !== databaseId || !values['from-file']) + (!values['writers-drained'] || + !values['project-writers-enabled'] || + !releaseRevision || + values['database-id'] !== databaseId || + !values['from-file']) + ) + throw new Error( + 'Apply requires --writers-drained, --project-writers-enabled, --release-revision, matching --database-id, and --from-file' + ) + if ( + mode !== 'apply' && + (values['writers-drained'] || + values['project-writers-enabled'] || + values['release-revision'] || + values['from-file']) ) - throw new Error('Apply requires --writers-drained, matching --database-id, and --from-file') - if (mode !== 'apply' && (values['writers-drained'] || values['from-file'])) throw new Error('Apply-only options used with read-only mode') + const operatorAssertions = + mode === 'apply' + ? { + oldWritersDrained: true, + projectWritersEnabled: true, + releaseRevision, + } + : undefined const inputPath = values['from-file'] if (inputPath && resolve(inputPath) === resolve(reportPath)) throw new Error('Keep the dry-run plan and apply report in separate files') @@ -118,7 +146,14 @@ async function main() { const partialPath = `${reportPath}.${process.pid}.partial` await writeFile( partialPath, - JSON.stringify({ databaseId, mode, completed: false, ready: false, stopped: 'Starting' }), + JSON.stringify({ + databaseId, + mode, + operatorAssertions, + completed: false, + ready: false, + stopped: 'Starting', + }), { mode: 0o600 } ) await rename(partialPath, reportPath) @@ -130,7 +165,9 @@ async function main() { maxFamilySize: Number(values['max-family-size']), shouldStop: () => interrupted, onProgress: async (progress) => { - await writeFile(partialPath, JSON.stringify(progress, null, 2), { mode: 0o600 }) + await writeFile(partialPath, JSON.stringify({ ...progress, operatorAssertions }, null, 2), { + mode: 0o600, + }) await rename(partialPath, reportPath) logger.info('Project backfill progress', { mode, diff --git a/packages/db/scripts/project-backfill.integration.ts b/packages/db/scripts/project-backfill.integration.ts index b60b960c7da..dc4a4c30e8c 100644 --- a/packages/db/scripts/project-backfill.integration.ts +++ b/packages/db/scripts/project-backfill.integration.ts @@ -344,6 +344,22 @@ describe('Project backfill CLI', () => { const dry = await run(['dry-run'], planPath) expect(dry.code, dry.stdout + dry.stderr).toBe(0) const plan = JSON.parse(await readFile(planPath, 'utf8')) as ProjectBackfillReport + for (const missing of [ + '--project-writers-enabled', + '--release-revision', + '--writers-drained', + ]) { + const args = ['apply', '--from-file', planPath, '--database-id', plan.databaseId] + if (missing !== '--writers-drained') args.push('--writers-drained') + if (missing !== '--project-writers-enabled') args.push('--project-writers-enabled') + if (missing !== '--release-revision') + args.push('--release-revision', 'integration-compatible-release') + const refused = await run(args) + expect(refused.code).toBe(1) + expect(refused.stdout + refused.stderr).toContain('Apply requires') + expect(await sql`SELECT * FROM project`).toHaveLength(0) + } + expect( ( await run([ @@ -351,6 +367,9 @@ describe('Project backfill CLI', () => { '--from-file', planPath, '--writers-drained', + '--project-writers-enabled', + '--release-revision', + 'integration-compatible-release', '--database-id', 'wrong', ]) @@ -364,11 +383,20 @@ describe('Project backfill CLI', () => { '--from-file', planPath, '--writers-drained', + '--project-writers-enabled', + '--release-revision', + 'integration-compatible-release', '--database-id', plan.databaseId, ]) ).code ).toBe(0) + const applied = JSON.parse(await readFile(reportPath, 'utf8')) + expect(applied.operatorAssertions).toEqual({ + oldWritersDrained: true, + projectWritersEnabled: true, + releaseRevision: 'integration-compatible-release', + }) expect((await run(['verify'])).code).toBe(0) const report = JSON.parse(await readFile(reportPath, 'utf8')) as ProjectBackfillReport expect(report.ready).toBe(true) @@ -380,6 +408,9 @@ describe('Project backfill CLI', () => { '--from-file', planPath, '--writers-drained', + '--project-writers-enabled', + '--release-revision', + 'integration-compatible-release', '--database-id', plan.databaseId, ], From 512134730ca7ac6b0f3c6f2d6a2e985ebe275fe9 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 15:13:44 -0700 Subject: [PATCH 006/113] feat(projects): enforce membership after the staged backfill --- .github/scripts/check-project-rollout.py | 81 + .github/workflows/ci.yml | 6 + .github/workflows/migrations.yml | 27 + apps/sim/lib/core/config/env.ts | 1 - apps/sim/lib/projects/README.md | 2 +- .../__integration__/foundation.integration.ts | 98 +- apps/sim/lib/projects/rollout.server.ts | 11 +- apps/sim/lib/workspaces/create.ts | 34 +- .../0394_project_membership_enforcement.sql | 207 + .../db/migrations/meta/0394_snapshot.json | 29715 ++++++++++++++++ packages/db/migrations/meta/_journal.json | 7 + packages/db/schema.ts | 2 +- .../db/scripts/project-contract-required.ts | 36 + .../scripts/project-contract.integration.ts | 270 + 14 files changed, 30444 insertions(+), 53 deletions(-) create mode 100644 .github/scripts/check-project-rollout.py create mode 100644 packages/db/migrations/0394_project_membership_enforcement.sql create mode 100644 packages/db/migrations/meta/0394_snapshot.json create mode 100644 packages/db/scripts/project-contract-required.ts create mode 100644 packages/db/scripts/project-contract.integration.ts diff --git a/.github/scripts/check-project-rollout.py b/.github/scripts/check-project-rollout.py new file mode 100644 index 00000000000..6c6c410b3ae --- /dev/null +++ b/.github/scripts/check-project-rollout.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +"""Read-only, fail-closed ECS retirement check for the Project contract migration. + +The expected digest is the operator's release-scoped acknowledgment that Project +writers are enabled, relevant old worker jobs are drained, and backfill verification +passed. AWS checks below independently verify ECS retirement, not those assertions. +""" +import argparse +import json +import re +import subprocess +import sys + + +def aws(region, *args): + result = subprocess.run( + ['aws', '--region', region, '--no-cli-pager', '--cli-connect-timeout', '10', '--cli-read-timeout', '30', *args, '--output', 'json'], + capture_output=True, text=True, timeout=90, check=False, + ) + if result.returncode: + raise RuntimeError('AWS preflight read failed; check deployment-read permissions') + return json.loads(result.stdout) + + +def verify(environment, region, digest): + if not re.fullmatch(r'sha256:[0-9a-f]{64}', digest): + raise RuntimeError('Set the environment-specific PROJECT_ENFORCEMENT_READY_IMAGE_DIGEST after reviewing rollout and backfill evidence') + pipeline = f'sim-{environment}-{region}-app-deployment' + executions = aws(region, 'codepipeline', 'list-pipeline-executions', '--pipeline-name', pipeline).get('pipelineExecutionSummaries', []) + if not executions or executions[0].get('status') != 'Succeeded': + raise RuntimeError('The latest app pipeline has not completed; traffic cutover alone is insufficient') + execution_id = executions[0]['pipelineExecutionId'] + group = aws(region, 'deploy', 'get-deployment-group', '--application-name', f'sim-{environment}-{region}-ecs-app', + '--deployment-group-name', f'sim-{environment}-{region}-app-dg')['deploymentGroupInfo'] + services = group.get('ecsServices', []) + if len(services) != 1: + raise RuntimeError('Expected exactly one application ECS service') + cluster, service = services[0]['clusterName'], services[0]['serviceName'] + description = aws(region, 'ecs', 'describe-services', '--cluster', cluster, '--services', service) + if description.get('failures') or len(description.get('services', [])) != 1: + raise RuntimeError('Cannot inspect the application ECS service') + record = description['services'][0] + if record.get('desiredCount', 0) < 1 or record.get('runningCount') != record['desiredCount'] or record.get('pendingCount') != 0: + raise RuntimeError('Application ECS service is not stable') + arns = set() + for status in ('RUNNING', 'STOPPED'): + arns.update(aws(region, 'ecs', 'list-tasks', '--cluster', cluster, '--service-name', service, + '--desired-status', status).get('taskArns', [])) + live = [] + ordered = sorted(arns) + for start in range(0, len(ordered), 100): + response = aws(region, 'ecs', 'describe-tasks', '--cluster', cluster, '--tasks', *ordered[start:start + 100]) + if response.get('failures'): + raise RuntimeError('Cannot account for every ECS task') + if len(response.get('tasks', [])) != len(ordered[start:start + 100]): + raise RuntimeError('Incomplete ECS task response') + live.extend(task for task in response['tasks'] if task.get('lastStatus') != 'STOPPED') + if len(live) != record['desiredCount']: + raise RuntimeError('Old, stopping, or pending ECS tasks remain') + for task in live: + app = [container for container in task.get('containers', []) if container.get('name') == 'app'] + if task.get('lastStatus') != 'RUNNING' or task.get('desiredStatus') != 'RUNNING' or len(app) != 1 or app[0].get('imageDigest') != digest: + raise RuntimeError('A live ECS task does not match the acknowledged compatible release') + latest = aws(region, 'codepipeline', 'list-pipeline-executions', '--pipeline-name', pipeline).get('pipelineExecutionSummaries', []) + if not latest or latest[0].get('pipelineExecutionId') != execution_id or latest[0].get('status') != 'Succeeded': + raise RuntimeError('Application deployment changed during preflight') + print(json.dumps({'ecsRetired': True, 'expectedImageDigest': digest, 'pipelineExecutionId': execution_id, + 'operatorAcknowledgedWritersWorkersAndBackfill': True})) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--environment', required=True, choices=['production', 'staging']) + parser.add_argument('--region', required=True) + parser.add_argument('--expected-image-digest', required=True) + args = parser.parse_args() + try: + verify(args.environment, args.region, args.expected_image_digest) + except (RuntimeError, ValueError, KeyError, TypeError, subprocess.TimeoutExpired) as error: + print(f'Project rollout preflight refused: {error}', file=sys.stderr) + sys.exit(1) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 547db6f3459..13c6fe4a07b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,6 +116,9 @@ jobs: # in place before the new app version deploys (replaces the removed ECS # migration sidecar) migrate: + permissions: + contents: read + id-token: write name: Migrate DB needs: [test-build] # Explicit need results instead of the implicit success(): a skipped job @@ -134,6 +137,9 @@ jobs: # Same ordering for dev (schema push before the dev image lands in ECR) migrate-dev: + permissions: + contents: read + id-token: write name: Migrate Dev DB if: github.event_name == 'push' && github.ref == 'refs/heads/dev' uses: ./.github/workflows/migrations.yml diff --git a/.github/workflows/migrations.yml b/.github/workflows/migrations.yml index 724972803be..7d634da683b 100644 --- a/.github/workflows/migrations.yml +++ b/.github/workflows/migrations.yml @@ -20,6 +20,7 @@ on: permissions: contents: read + id-token: write jobs: migrate: @@ -51,6 +52,32 @@ jobs: - name: Install dependencies run: bun install --frozen-lockfile --ignore-scripts + - name: Check whether Project enforcement needs rollout evidence + id: project-contract + if: inputs.environment != 'dev' + working-directory: ./packages/db + env: + DATABASE_URL: ${{ inputs.environment == 'production' && secrets.DATABASE_URL || inputs.environment == 'staging' && secrets.STAGING_DATABASE_URL || '' }} + MIGRATION_DATABASE_URL: ${{ inputs.environment == 'production' && secrets.MIGRATION_DATABASE_URL || inputs.environment == 'staging' && secrets.STAGING_MIGRATION_DATABASE_URL || '' }} + run: bun --no-env-file scripts/project-contract-required.ts >> "$GITHUB_OUTPUT" + + - name: Configure AWS for Project retirement verification + if: steps.project-contract.outputs.required == 'true' + uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b + with: + role-to-assume: ${{ inputs.environment == 'production' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }} + aws-region: ${{ inputs.environment == 'production' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }} + + # Set this release-scoped acknowledgment only after writers are enabled everywhere, + # relevant old Trigger.dev runs are drained, and the reviewed backfill verifies ready. + # The preflight independently checks ECS retirement; it does not inspect worker runs. + - name: Require completed compatible rollout before Project enforcement + if: steps.project-contract.outputs.required == 'true' + env: + ENVIRONMENT: ${{ inputs.environment }} + EXPECTED_IMAGE_DIGEST: ${{ inputs.environment == 'production' && vars.PROJECT_ENFORCEMENT_READY_IMAGE_DIGEST_PRODUCTION || inputs.environment == 'staging' && vars.PROJECT_ENFORCEMENT_READY_IMAGE_DIGEST_STAGING || '' }} + run: python3 .github/scripts/check-project-rollout.py --environment "$ENVIRONMENT" --region "$AWS_REGION" --expected-image-digest "$EXPECTED_IMAGE_DIGEST" + # The expression maps the explicit environment input to exactly one repo # secret, so the job never holds another environment's database URL. An # unknown environment resolves to empty and the guard below fails the job. diff --git a/apps/sim/lib/core/config/env.ts b/apps/sim/lib/core/config/env.ts index 2fa5809732f..9904b0143e3 100644 --- a/apps/sim/lib/core/config/env.ts +++ b/apps/sim/lib/core/config/env.ts @@ -669,7 +669,6 @@ export const env = createEnv({ // SSO Configuration (for script-based registration) SSO_ENABLED: z.boolean().optional(), // Enable SSO functionality - PROJECT_WRITES_ENABLED: z.boolean().optional(), // Activate new Project assignments after old writers drain PROJECT_API_ENABLED: z.boolean().optional(), // Expose Projects after backfill and contract enforcement SCIM_ENABLED: z.boolean().optional(), // Enable SCIM directory provisioning USAGE_MONITORING_ENABLED: z.boolean().optional(), // Enable organization usage monitoring on self-hosted (bypasses hosted requirements) diff --git a/apps/sim/lib/projects/README.md b/apps/sim/lib/projects/README.md index fb81f7dbb26..942595815e7 100644 --- a/apps/sim/lib/projects/README.md +++ b/apps/sim/lib/projects/README.md @@ -2,7 +2,7 @@ A Project groups environments. An environment is an existing `workspace` record; there is no separate environment table. Every newly created Project starts with an environment. -Project APIs return HTTP 503 until the deployment enables `PROJECT_API_ENABLED`. Automatic assignment on legacy workspace creation requires `PROJECT_WRITES_ENABLED`; both server controls default off. The API requires writers to be enabled. Existing assigned Projects always retain their lifecycle protections, including fork inheritance and disconnect behavior, even if activation is disabled. +Project APIs return HTTP 503 until the deployment enables `PROJECT_API_ENABLED`. Workspace creation always assigns a Project atomically. The API control defaults off and does not disable assignment. Existing assigned Projects always retain their lifecycle protections, including fork inheritance and disconnect behavior, even if activation is disabled. ## Choose the creation flow diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 839f2190d5e..e7fe2cf21d1 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -1,4 +1,4 @@ -import { mkdir, writeFile } from 'node:fs/promises' +import { mkdir, readFile, writeFile } from 'node:fs/promises' import { dirname, resolve } from 'node:path' import { db } from '@sim/db' import { @@ -18,6 +18,7 @@ import { workspaceForkPromoteRun, workspaceForkResourceMap, } from '@sim/db/schema' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { createSessionPrincipal, createWorkspaceApiKeyPrincipal, @@ -27,6 +28,7 @@ import { getErrorMessage, getPostgresErrorCode } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' +import postgres from 'postgres' import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' import { removeUserFromOrganization } from '@/lib/billing/organizations/membership' import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' @@ -194,7 +196,79 @@ afterAll(async () => { describe('Project foundation at the database and application boundary', () => { check( - 'disabled rollout preserves legacy creation and fork/disconnect without Project rows', + 'application creation, disconnect, organization deletion and archive commit with SQL enforcement', + async () => { + const client = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => undefined }) + try { + await client.unsafe( + await readFile( + new URL( + '../../../../../packages/db/migrations/0394_project_membership_enforcement.sql', + import.meta.url + ), + 'utf8' + ) + ) + const f = await fixture(true, 1) + const organizationId = f.organizationId + if (!organizationId) throw new Error('Missing organization fixture') + const created = await createProject.execute({ + principal: f.owner, + input: { + organizationId: f.organizationId, + name: 'Enforced', + initialEnvironment: { name: 'Production' }, + }, + request, + }) + environments.push(created.initialEnvironment.id) + const source = await getWorkspaceWithOwner(created.initialEnvironment.id) + if (!source) throw new Error('Missing source environment') + const fork = await createFork({ + source, + policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), + userId: f.ownerId, + name: 'Staging', + }) + environments.push(fork.workspace.id) + await unlinkForkEdge({ parentWorkspaceId: source.id, childWorkspaceId: fork.workspace.id }) + const [detached] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) + expect(detached.projectId).not.toBe(created.project.id) + await db.transaction(async (tx) => { + await detachOrganizationWorkspacesTx(tx, organizationId) + await tx.delete(organization).where(eq(organization.id, organizationId)) + }) + await archiveProject.execute({ + principal: f.owner, + input: { projectId: created.project.id }, + request, + }) + const [archived] = await db.select().from(project).where(eq(project.id, created.project.id)) + expect(archived.organizationId).toBeNull() + expect(archived.archivedAt).not.toBeNull() + expect( + await db + .select() + .from(workflow) + .where(and(eq(workflow.workspaceId, source.id), sql`${workflow.archivedAt} IS NULL`)) + ).toHaveLength(0) + } finally { + await client.unsafe('ROLLBACK') + for (const table of ['project', 'project_workspace', 'workspace', 'workflow']) { + await client.unsafe( + `DROP TRIGGER IF EXISTS project_contract_lock ON ${table}; DROP TRIGGER IF EXISTS project_contract_check ON ${table}` + ) + } + await client.end() + } + } + ) + + check( + 'workspace creation and fork/disconnect assign Projects even with the retired writer flag off', async () => { vi.stubEnv('PROJECT_WRITES_ENABLED', 'false') vi.stubEnv('PROJECT_API_ENABLED', 'false') @@ -214,7 +288,7 @@ describe('Project foundation at the database and application boundary', () => { environments.push(source.id) expect( await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, source.id)) - ).toEqual([]) + ).toHaveLength(1) const parent = await getWorkspaceWithOwner(source.id) if (!parent) throw new Error('Missing source fixture') const fork = await createFork({ @@ -229,11 +303,11 @@ describe('Project foundation at the database and application boundary', () => { .select() .from(projectWorkspace) .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) - ).toEqual([]) + ).toHaveLength(1) await unlinkForkEdge({ parentWorkspaceId: source.id, childWorkspaceId: fork.workspace.id }) const [child] = await db.select().from(workspace).where(eq(workspace.id, fork.workspace.id)) expect(child.forkedFromWorkspaceId).toBeNull() - expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(1) + expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(3) } ) @@ -316,7 +390,7 @@ describe('Project foundation at the database and application boundary', () => { } ) - check('writer activation assigns new workspaces while Project APIs remain disabled', async () => { + check('new workspaces receive Projects while Project APIs remain disabled', async () => { vi.stubEnv('PROJECT_API_ENABLED', 'false') const f = await fixture(false, 1) const created = await db.transaction((tx) => @@ -335,18 +409,6 @@ describe('Project foundation at the database and application boundary', () => { expect( await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, created.id)) ).toHaveLength(1) - vi.stubEnv('PROJECT_WRITES_ENABLED', 'false') - vi.stubEnv('PROJECT_API_ENABLED', 'true') - await expect( - createProject.execute({ - principal: f.owner, - input: { organizationId: null, name: 'Invalid', initialEnvironment: { name: 'First' } }, - request, - }) - ).rejects.toThrow('PROJECT_API_ENABLED requires PROJECT_WRITES_ENABLED') - expect(await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId))).toHaveLength( - 2 - ) }) check('legacy fork and disconnect refuse a partially assigned subtree', async () => { diff --git a/apps/sim/lib/projects/rollout.server.ts b/apps/sim/lib/projects/rollout.server.ts index 3ffe5d81e8c..1f6e24962e4 100644 --- a/apps/sim/lib/projects/rollout.server.ts +++ b/apps/sim/lib/projects/rollout.server.ts @@ -1,15 +1,6 @@ import { envBoolean, getEnv } from '@/lib/core/config/env' import { HttpError } from '@/lib/core/utils/http-error' -/** Deployment-wide controls; existing Project lifecycle maintenance never depends on these. */ -export function getProjectRollout() { - const writesEnabled = envBoolean(getEnv('PROJECT_WRITES_ENABLED')) ?? false - const apiEnabled = envBoolean(getEnv('PROJECT_API_ENABLED')) ?? false - if (apiEnabled && !writesEnabled) - throw new Error('PROJECT_API_ENABLED requires PROJECT_WRITES_ENABLED') - return { writesEnabled, apiEnabled } -} - class ProjectUnavailableError extends HttpError { readonly statusCode = 503 constructor() { @@ -18,5 +9,5 @@ class ProjectUnavailableError extends HttpError { } export function requireProjectApiEnabled(): void { - if (!getProjectRollout().apiEnabled) throw new ProjectUnavailableError() + if (!(envBoolean(getEnv('PROJECT_API_ENABLED')) ?? false)) throw new ProjectUnavailableError() } diff --git a/apps/sim/lib/workspaces/create.ts b/apps/sim/lib/workspaces/create.ts index d81847a487f..4b1549bd0ae 100644 --- a/apps/sim/lib/workspaces/create.ts +++ b/apps/sim/lib/workspaces/create.ts @@ -6,7 +6,7 @@ import { generateId } from '@sim/utils/id' import { PlatformEvents } from '@/lib/core/telemetry' import type { DbTransaction } from '@/lib/db/types' import { createProjectForWorkspace } from '@/lib/projects/membership' -import { getProjectRollout, requireProjectApiEnabled } from '@/lib/projects/rollout.server' +import { requireProjectApiEnabled } from '@/lib/projects/rollout.server' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' @@ -94,7 +94,6 @@ async function createWorkspaceRecordsInTransaction( tx: DbTransaction, { projectName, - assignProject, userId, observedOrganizationId, name, @@ -103,8 +102,8 @@ async function createWorkspaceRecordsInTransaction( workspaceMode, billedAccountUserId, governingPermissionGroupOrganizationId, - }: TransactionalCreateWorkspaceParams & { projectName?: string; assignProject: boolean } -): Promise<{ projectId: string | null; workspace: CreatedWorkspace }> { + }: TransactionalCreateWorkspaceParams & { projectName?: string } +): Promise<{ projectId: string; workspace: CreatedWorkspace }> { const workspaceId = generateId() const workflowId = generateId() const now = new Date() @@ -133,15 +132,13 @@ async function createWorkspaceRecordsInTransaction( updatedAt: now, }) - const projectId = assignProject - ? await createProjectForWorkspace(tx, { - projectName, - workspaceId, - name, - organizationId: organizationId ?? null, - ownerId: userId, - }) - : null + const projectId = await createProjectForWorkspace(tx, { + projectName, + workspaceId, + name, + organizationId: organizationId ?? null, + ownerId: userId, + }) const permissionRows = [ { @@ -213,9 +210,7 @@ export async function createWorkspaceWithProjectInTransaction( params: TransactionalCreateWorkspaceParams & { projectName?: string } ): Promise<{ projectId: string; workspace: CreatedWorkspace }> { requireProjectApiEnabled() - const created = await createWorkspaceRecordsInTransaction(tx, { ...params, assignProject: true }) - if (!created.projectId) throw new Error('Project creation did not assign a Project') - return { ...created, projectId: created.projectId } + return createWorkspaceRecordsInTransaction(tx, params) } /** Preserves the workspace-only result for existing creation callers. */ @@ -223,12 +218,7 @@ export async function createWorkspaceInTransaction( tx: DbTransaction, params: TransactionalCreateWorkspaceParams ): Promise { - return ( - await createWorkspaceRecordsInTransaction(tx, { - ...params, - assignProject: getProjectRollout().writesEnabled, - }) - ).workspace + return (await createWorkspaceRecordsInTransaction(tx, params)).workspace } /** Creates a workspace through the canonical lock-and-insert transaction. */ diff --git a/packages/db/migrations/0394_project_membership_enforcement.sql b/packages/db/migrations/0394_project_membership_enforcement.sql new file mode 100644 index 00000000000..92cbd512c4d --- /dev/null +++ b/packages/db/migrations/0394_project_membership_enforcement.sql @@ -0,0 +1,207 @@ +BEGIN; +--> statement-breakpoint +SET LOCAL lock_timeout = '5s'; +--> statement-breakpoint +SET LOCAL statement_timeout = '60s'; +--> statement-breakpoint +LOCK TABLE workspace, project, project_workspace, workflow IN SHARE ROW EXCLUSIVE MODE; +--> statement-breakpoint +DO $$ +BEGIN + IF EXISTS (SELECT 1 FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id WHERE pw.workspace_id IS NULL) THEN + RAISE EXCEPTION 'Project enforcement requires the reviewed Project backfill: missing environment memberships' USING ERRCODE = '55000'; + END IF; + IF EXISTS ( + SELECT 1 FROM project p LEFT JOIN project_workspace pw ON pw.project_id = p.id + LEFT JOIN workspace w ON w.id = pw.workspace_id + GROUP BY p.id HAVING count(w.id) = 0 + OR (p.archived_at IS NULL AND count(w.id) FILTER (WHERE w.archived_at IS NULL) = 0) + OR (p.archived_at IS NOT NULL AND count(w.id) FILTER (WHERE w.archived_at IS NULL) > 0) + ) THEN + RAISE EXCEPTION 'Project enforcement requires nonempty Projects with consistent archive state; reconcile and verify the backfill' USING ERRCODE = '55000'; + END IF; + IF EXISTS ( + SELECT 1 FROM project_workspace pw JOIN project p ON p.id = pw.project_id + JOIN workspace w ON w.id = pw.workspace_id WHERE w.organization_id IS DISTINCT FROM p.organization_id + ) OR EXISTS ( + SELECT 1 FROM workspace w JOIN project_workspace child ON child.workspace_id = w.id + LEFT JOIN project_workspace parent ON parent.workspace_id = w.forked_from_workspace_id + WHERE w.forked_from_workspace_id IS NOT NULL AND parent.project_id IS DISTINCT FROM child.project_id + ) THEN + RAISE EXCEPTION 'Project enforcement requires reconciled organization scope and fork membership' USING ERRCODE = '55000'; + END IF; + IF EXISTS ( + SELECT 1 FROM project p JOIN project_workspace pw ON pw.project_id = p.id + JOIN workflow f ON f.workspace_id = pw.workspace_id + WHERE p.archived_at IS NOT NULL AND f.archived_at IS NULL + ) THEN + RAISE EXCEPTION 'Project enforcement requires archived Projects to have no active workflows' USING ERRCODE = '55000'; + END IF; +END; +$$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_contract_assert_project(target_id text) RETURNS void +LANGUAGE plpgsql AS $$ +DECLARE + record project%ROWTYPE; + total bigint; + active bigint; +BEGIN + SELECT * INTO record FROM project WHERE id = target_id; + IF NOT FOUND THEN RETURN; END IF; + SELECT count(*), count(*) FILTER (WHERE w.archived_at IS NULL) + INTO total, active FROM project_workspace pw JOIN workspace w ON w.id = pw.workspace_id + WHERE pw.project_id = target_id; + IF total = 0 OR (record.archived_at IS NULL AND active = 0) + OR (record.archived_at IS NOT NULL AND active > 0) THEN + RAISE EXCEPTION 'Project must retain environments with consistent archive state' + USING ERRCODE = '23514', CONSTRAINT = 'project_environment_lifecycle'; + END IF; + IF EXISTS ( + SELECT 1 FROM project_workspace pw JOIN workspace w ON w.id = pw.workspace_id + WHERE pw.project_id = target_id AND w.organization_id IS DISTINCT FROM record.organization_id + ) THEN + RAISE EXCEPTION 'Project and environment organization must agree' + USING ERRCODE = '23514', CONSTRAINT = 'project_environment_organization'; + END IF; + IF record.archived_at IS NOT NULL AND EXISTS ( + SELECT 1 FROM project_workspace pw JOIN workflow f ON f.workspace_id = pw.workspace_id + WHERE pw.project_id = target_id AND f.archived_at IS NULL + ) THEN + RAISE EXCEPTION 'Archived Project cannot contain active workflows' + USING ERRCODE = '23514', CONSTRAINT = 'project_workflow_lifecycle'; + END IF; +END; +$$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_contract_assert_workspace(target_id text) RETURNS void +LANGUAGE plpgsql AS $$ +DECLARE + parent_id text; + member_project_id text; + parent_project_id text; +BEGIN + SELECT forked_from_workspace_id INTO parent_id FROM workspace WHERE id = target_id; + IF NOT FOUND THEN RETURN; END IF; + SELECT project_id INTO member_project_id FROM project_workspace WHERE workspace_id = target_id; + IF member_project_id IS NULL THEN + RAISE EXCEPTION 'Environment must belong to exactly one Project' + USING ERRCODE = '23514', CONSTRAINT = 'workspace_project_required'; + END IF; + IF parent_id IS NOT NULL THEN + SELECT project_id INTO parent_project_id FROM project_workspace WHERE workspace_id = parent_id; + IF parent_project_id IS DISTINCT FROM member_project_id THEN + RAISE EXCEPTION 'Connected fork environments must belong to the same Project' + USING ERRCODE = '23514', CONSTRAINT = 'workspace_fork_project'; + END IF; + END IF; + IF EXISTS ( + SELECT 1 FROM workspace child LEFT JOIN project_workspace pw ON pw.workspace_id = child.id + WHERE child.forked_from_workspace_id = target_id AND pw.project_id IS DISTINCT FROM member_project_id + ) THEN + RAISE EXCEPTION 'Connected fork environments must belong to the same Project' + USING ERRCODE = '23514', CONSTRAINT = 'workspace_fork_project'; + END IF; + PERFORM project_contract_assert_project(member_project_id); +END; +$$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_contract_lock_projects(target_ids text[]) RETURNS void +LANGUAGE plpgsql AS $$ +DECLARE + target_id text; +BEGIN + FOR target_id IN SELECT DISTINCT id FROM unnest(target_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP + PERFORM pg_advisory_xact_lock(hashtextextended('project:' || target_id, 0)); + -- Touch the row so repeatable-read transactions serialize rather than accepting a stale environment count. + UPDATE project SET updated_at = updated_at WHERE id = target_id; + END LOOP; +END; +$$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_contract_before_write() RETURNS trigger +LANGUAGE plpgsql AS $$ +DECLARE + previous_id text; + next_id text; + owners text[]; +BEGIN + IF TG_TABLE_NAME = 'project' THEN + IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; + PERFORM pg_advisory_xact_lock(hashtextextended('project:' || coalesce(next_id, previous_id), 0)); + ELSIF TG_TABLE_NAME = 'project_workspace' THEN + IF TG_OP <> 'INSERT' THEN previous_id := OLD.project_id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.project_id; END IF; + PERFORM project_contract_lock_projects(ARRAY[previous_id, next_id]); + ELSE + IF TG_TABLE_NAME = 'workspace' THEN + IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; + ELSE + IF TG_OP <> 'INSERT' THEN previous_id := OLD.workspace_id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.workspace_id; END IF; + END IF; + SELECT array_agg(project_id) INTO owners FROM project_workspace WHERE workspace_id IN (previous_id, next_id); + PERFORM project_contract_lock_projects(owners); + END IF; + IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; + RETURN NEW; +END; +$$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_contract_after_write() RETURNS trigger +LANGUAGE plpgsql AS $$ +BEGIN + IF TG_TABLE_NAME = 'project' THEN + IF TG_OP <> 'INSERT' THEN PERFORM project_contract_assert_project(OLD.id); END IF; + IF TG_OP <> 'DELETE' THEN PERFORM project_contract_assert_project(NEW.id); END IF; + ELSIF TG_TABLE_NAME = 'project_workspace' THEN + IF TG_OP <> 'INSERT' THEN + PERFORM project_contract_assert_project(OLD.project_id); + PERFORM project_contract_assert_workspace(OLD.workspace_id); + END IF; + IF TG_OP <> 'DELETE' THEN + PERFORM project_contract_assert_project(NEW.project_id); + PERFORM project_contract_assert_workspace(NEW.workspace_id); + END IF; + ELSIF TG_TABLE_NAME = 'workspace' THEN + IF TG_OP <> 'INSERT' THEN PERFORM project_contract_assert_workspace(OLD.id); END IF; + IF TG_OP <> 'DELETE' THEN PERFORM project_contract_assert_workspace(NEW.id); END IF; + ELSE + IF TG_OP <> 'INSERT' THEN PERFORM project_contract_assert_workspace(OLD.workspace_id); END IF; + IF TG_OP <> 'DELETE' THEN PERFORM project_contract_assert_workspace(NEW.workspace_id); END IF; + END IF; + RETURN NULL; +END; +$$; +--> statement-breakpoint +DROP TRIGGER IF EXISTS project_contract_lock ON project; +CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OR DELETE ON project +FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); +DROP TRIGGER IF EXISTS project_contract_check ON project; +CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OR DELETE ON project +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); +--> statement-breakpoint +DROP TRIGGER IF EXISTS project_contract_lock ON project_workspace; +CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OR DELETE ON project_workspace +FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); +DROP TRIGGER IF EXISTS project_contract_check ON project_workspace; +CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OR DELETE ON project_workspace +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); +--> statement-breakpoint +DROP TRIGGER IF EXISTS project_contract_lock ON workspace; +CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OF id, archived_at, organization_id, forked_from_workspace_id OR DELETE ON workspace +FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); +DROP TRIGGER IF EXISTS project_contract_check ON workspace; +CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OF id, archived_at, organization_id, forked_from_workspace_id OR DELETE ON workspace +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); +--> statement-breakpoint +DROP TRIGGER IF EXISTS project_contract_lock ON workflow; +CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OF workspace_id, archived_at OR DELETE ON workflow +FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); +DROP TRIGGER IF EXISTS project_contract_check ON workflow; +CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OF workspace_id, archived_at OR DELETE ON workflow +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); +--> statement-breakpoint +COMMIT; diff --git a/packages/db/migrations/meta/0394_snapshot.json b/packages/db/migrations/meta/0394_snapshot.json new file mode 100644 index 00000000000..81c70efc0c7 --- /dev/null +++ b/packages/db/migrations/meta/0394_snapshot.json @@ -0,0 +1,29715 @@ +{ + "id": "e59716b2-6689-4cfa-83b8-8fe3a89b77e3", + "prevId": "ba7c9a7d-a315-42bc-9de6-931239aa626b", + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "academy_certificate_course_id_idx": { + "name": "academy_certificate_course_id_idx", + "columns": [ + { + "expression": "course_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "academy_certificate_status_idx": { + "name": "academy_certificate_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "academy_certificate_user_id_user_id_fk": { + "name": "academy_certificate_user_id_user_id_fk", + "tableFrom": "academy_certificate", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "academy_certificate_certificate_number_unique": { + "name": "academy_certificate_certificate_number_unique", + "columns": ["certificate_number"], + "nullsNotDistinct": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "agent_memory_turn_workflow_idx": { + "name": "agent_memory_turn_workflow_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "agent_memory_turn_memory_id_memory_id_fk": { + "name": "agent_memory_turn_memory_id_memory_id_fk", + "tableFrom": "agent_memory_turn", + "columnsFrom": ["memory_id"], + "tableTo": "memory", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "agent_memory_turn_workflow_id_workflow_id_fk": { + "name": "agent_memory_turn_workflow_id_workflow_id_fk", + "tableFrom": "agent_memory_turn", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "api_key_key_hash_idx": { + "name": "api_key_key_hash_idx", + "columns": [ + { + "expression": "key_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "api_key_user_id_user_id_fk": { + "name": "api_key_user_id_user_id_fk", + "tableFrom": "api_key", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "api_key_workspace_id_workspace_id_fk": { + "name": "api_key_workspace_id_workspace_id_fk", + "tableFrom": "api_key", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "api_key_created_by_user_id_fk": { + "name": "api_key_created_by_user_id_fk", + "tableFrom": "api_key", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_key_key_unique": { + "name": "api_key_key_unique", + "columns": ["key"], + "nullsNotDistinct": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"async_jobs\".\"type\" = 'schedule-execution' AND \"async_jobs\".\"status\" = 'pending'", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"async_jobs\".\"type\" = 'schedule-execution' AND \"async_jobs\".\"status\" = 'processing'", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"async_jobs\".\"type\" = 'schedule-execution' AND \"async_jobs\".\"status\" IN ('completed', 'failed', 'cancelled') AND COALESCE(\"async_jobs\".\"metadata\" ->> 'scheduleReconciled', 'false') <> 'true'", + "concurrently": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "audit_log_action_idx": { + "name": "audit_log_action_idx", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "audit_log_workspace_id_workspace_id_fk": { + "name": "audit_log_workspace_id_workspace_id_fk", + "tableFrom": "audit_log", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "audit_log_actor_id_user_id_fk": { + "name": "audit_log_actor_id_user_id_fk", + "tableFrom": "audit_log", + "columnsFrom": ["actor_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "background_work_status_meta_child_ws_idx": { + "name": "background_work_status_meta_child_ws_idx", + "columns": [ + { + "expression": "(\"metadata\" ->> 'childWorkspaceId')", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "background_work_status_meta_other_ws_idx": { + "name": "background_work_status_meta_other_ws_idx", + "columns": [ + { + "expression": "(\"metadata\" ->> 'otherWorkspaceId')", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "background_work_status_workspace_id_workspace_id_fk": { + "name": "background_work_status_workspace_id_workspace_id_fk", + "tableFrom": "background_work_status", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "background_work_status_workflow_id_workflow_id_fk": { + "name": "background_work_status_workflow_id_workflow_id_fk", + "tableFrom": "background_work_status", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "where": "\"chat\".\"archived_at\" IS NULL", + "concurrently": false + }, + "chat_archived_at_partial_idx": { + "name": "chat_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"chat\".\"archived_at\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "chat_workflow_id_workflow_id_fk": { + "name": "chat_workflow_id_workflow_id_fk", + "tableFrom": "chat", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "chat_user_id_user_id_fk": { + "name": "chat_user_id_user_id_fk", + "tableFrom": "chat", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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 + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_async_tool_calls_status_idx": { + "name": "copilot_async_tool_calls_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["run_id"], + "tableTo": "copilot_runs", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["checkpoint_id"], + "tableTo": "copilot_run_checkpoints", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_chats_external_conversation_unique": { + "name": "copilot_chats_external_conversation_unique", + "columns": [ + { + "expression": "external_conversation_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"copilot_chats\".\"external_conversation_key\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_chats_user_id_idx": { + "name": "copilot_chats_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_chats_workflow_id_idx": { + "name": "copilot_chats_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_chats_created_at_idx": { + "name": "copilot_chats_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_chats_updated_at_idx": { + "name": "copilot_chats_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"copilot_chats\".\"deleted_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "copilot_chats_user_id_user_id_fk": { + "name": "copilot_chats_user_id_user_id_fk", + "tableFrom": "copilot_chats", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_chats_workflow_id_workflow_id_fk": { + "name": "copilot_chats_workflow_id_workflow_id_fk", + "tableFrom": "copilot_chats", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_chats_workspace_id_workspace_id_fk": { + "name": "copilot_chats_workspace_id_workspace_id_fk", + "tableFrom": "copilot_chats", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_chats_organization_id_organization_id_fk": { + "name": "copilot_chats_organization_id_organization_id_fk", + "tableFrom": "copilot_chats", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_feedback_chat_id_idx": { + "name": "copilot_feedback_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_feedback_is_positive_idx": { + "name": "copilot_feedback_is_positive_idx", + "columns": [ + { + "expression": "is_positive", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_feedback_created_at_idx": { + "name": "copilot_feedback_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "copilot_feedback_user_id_user_id_fk": { + "name": "copilot_feedback_user_id_user_id_fk", + "tableFrom": "copilot_feedback", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_feedback_chat_id_copilot_chats_id_fk": { + "name": "copilot_feedback_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_feedback", + "columnsFrom": ["chat_id"], + "tableTo": "copilot_chats", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"copilot_messages\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"copilot_messages\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"copilot_messages\".\"stream_id\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"copilot_messages\".\"role\" = 'user' AND \"copilot_messages\".\"deleted_at\" IS NULL", + "concurrently": false + } + }, + "foreignKeys": { + "copilot_messages_chat_id_copilot_chats_id_fk": { + "name": "copilot_messages_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_messages", + "columnsFrom": ["chat_id"], + "tableTo": "copilot_chats", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_organization_request_stops_organization_id_organization_id_fk": { + "name": "copilot_organization_request_stops_organization_id_organization_id_fk", + "tableFrom": "copilot_organization_request_stops", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_request_stops_workspace_id_workspace_id_fk": { + "name": "copilot_request_stops_workspace_id_workspace_id_fk", + "tableFrom": "copilot_request_stops", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "copilot_run_checkpoints_run_id_copilot_runs_id_fk": { + "name": "copilot_run_checkpoints_run_id_copilot_runs_id_fk", + "tableFrom": "copilot_run_checkpoints", + "columnsFrom": ["run_id"], + "tableTo": "copilot_runs", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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": "'{}'" + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_runs_chat_id_idx": { + "name": "copilot_runs_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_runs_user_id_idx": { + "name": "copilot_runs_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_runs_workflow_id_idx": { + "name": "copilot_runs_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_runs_workspace_id_idx": { + "name": "copilot_runs_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_runs_status_idx": { + "name": "copilot_runs_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_runs_stream_id_unique": { + "name": "copilot_runs_stream_id_unique", + "columns": [ + { + "expression": "stream_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "copilot_runs_chat_id_copilot_chats_id_fk": { + "name": "copilot_runs_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_runs", + "columnsFrom": ["chat_id"], + "tableTo": "copilot_chats", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_runs_user_id_user_id_fk": { + "name": "copilot_runs_user_id_user_id_fk", + "tableFrom": "copilot_runs", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_runs_workflow_id_workflow_id_fk": { + "name": "copilot_runs_workflow_id_workflow_id_fk", + "tableFrom": "copilot_runs", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_runs_workspace_id_workspace_id_fk": { + "name": "copilot_runs_workspace_id_workspace_id_fk", + "tableFrom": "copilot_runs", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_runs_organization_id_organization_id_fk": { + "name": "copilot_runs_organization_id_organization_id_fk", + "tableFrom": "copilot_runs", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "where": "delivered_at IS NULL", + "concurrently": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "copilot_task_subscriptions_task_idx": { + "name": "copilot_task_subscriptions_task_idx", + "columns": [ + { + "expression": "task_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "copilot_task_subscriptions_chat_id_copilot_chats_id_fk": { + "name": "copilot_task_subscriptions_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_task_subscriptions", + "columnsFrom": ["chat_id"], + "tableTo": "copilot_chats", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_task_subscriptions_workspace_id_workspace_id_fk": { + "name": "copilot_task_subscriptions_workspace_id_workspace_id_fk", + "tableFrom": "copilot_task_subscriptions", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_task_subscriptions_user_id_user_id_fk": { + "name": "copilot_task_subscriptions_user_id_user_id_fk", + "tableFrom": "copilot_task_subscriptions", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["chat_id"], + "tableTo": "copilot_chats", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "copilot_workflow_read_hashes_workflow_id_workflow_id_fk": { + "name": "copilot_workflow_read_hashes_workflow_id_workflow_id_fk", + "tableFrom": "copilot_workflow_read_hashes", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"credential\".\"account_id\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"credential\".\"type\" = 'personal_token'", + "concurrently": false + }, + "credential_workspace_id_idx": { + "name": "credential_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_type_idx": { + "name": "credential_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_provider_id_idx": { + "name": "credential_provider_id_idx", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_account_id_idx": { + "name": "credential_account_id_idx", + "columns": [ + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_group_enrollment_idx": { + "name": "credential_group_enrollment_idx", + "columns": [ + { + "expression": "credential_group_enrollment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_mcp_server_idx": { + "name": "credential_mcp_server_idx", + "columns": [ + { + "expression": "mcp_server_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"credential\".\"type\" = 'managed_oauth'", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"credential\".\"type\" = 'managed_mcp'", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "account_id IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "type = 'env_workspace'", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "type = 'env_personal'", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "type = 'personal_token'", + "concurrently": false + } + }, + "foreignKeys": { + "credential_workspace_id_workspace_id_fk": { + "name": "credential_workspace_id_workspace_id_fk", + "tableFrom": "credential", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_organization_id_organization_id_fk": { + "name": "credential_organization_id_organization_id_fk", + "tableFrom": "credential", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_slack_app_id_slack_app_id_fk": { + "name": "credential_slack_app_id_slack_app_id_fk", + "tableFrom": "credential", + "columnsFrom": ["slack_app_id"], + "tableTo": "slack_app", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "no action" + }, + "credential_account_id_account_id_fk": { + "name": "credential_account_id_account_id_fk", + "tableFrom": "credential", + "columnsFrom": ["account_id"], + "tableTo": "account", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_env_owner_user_id_user_id_fk": { + "name": "credential_env_owner_user_id_user_id_fk", + "tableFrom": "credential", + "columnsFrom": ["env_owner_user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_credential_group_enrollment_id_credential_group_enrollment_id_fk": { + "name": "credential_credential_group_enrollment_id_credential_group_enrollment_id_fk", + "tableFrom": "credential", + "columnsFrom": ["credential_group_enrollment_id"], + "tableTo": "credential_group_enrollment", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_mcp_server_id_mcp_servers_id_fk": { + "name": "credential_mcp_server_id_mcp_servers_id_fk", + "tableFrom": "credential", + "columnsFrom": ["mcp_server_id"], + "tableTo": "mcp_servers", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_created_by_user_id_fk": { + "name": "credential_created_by_user_id_fk", + "tableFrom": "credential", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_group_organization_unique": { + "name": "credential_group_organization_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_group_public_id_unique": { + "name": "credential_group_public_id_unique", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_group_workspace_unique": { + "name": "credential_group_workspace_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "credential_group_workspace_id_workspace_id_fk": { + "name": "credential_group_workspace_id_workspace_id_fk", + "tableFrom": "credential_group", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_group_organization_id_organization_id_fk": { + "name": "credential_group_organization_id_organization_id_fk", + "tableFrom": "credential_group", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_group_created_by_user_id_fk": { + "name": "credential_group_created_by_user_id_fk", + "tableFrom": "credential_group", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "where": "\"credential_group_enrollment\".\"user_id\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["credential_group_id"], + "tableTo": "credential_group", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_group_enrollment_user_id_user_id_fk": { + "name": "credential_group_enrollment_user_id_user_id_fk", + "tableFrom": "credential_group_enrollment", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_group_enrollment_created_by_user_id_fk": { + "name": "credential_group_enrollment_created_by_user_id_fk", + "tableFrom": "credential_group_enrollment", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_member_role_idx": { + "name": "credential_member_role_idx", + "columns": [ + { + "expression": "role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "credential_member_status_idx": { + "name": "credential_member_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "credential_member_credential_id_credential_id_fk": { + "name": "credential_member_credential_id_credential_id_fk", + "tableFrom": "credential_member", + "columnsFrom": ["credential_id"], + "tableTo": "credential", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_member_user_id_user_id_fk": { + "name": "credential_member_user_id_user_id_fk", + "tableFrom": "credential_member", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "credential_member_invited_by_user_id_fk": { + "name": "credential_member_invited_by_user_id_fk", + "tableFrom": "credential_member", + "columnsFrom": ["invited_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "custom_block_workflow_id_idx": { + "name": "custom_block_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "custom_block_organization_id_organization_id_fk": { + "name": "custom_block_organization_id_organization_id_fk", + "tableFrom": "custom_block", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "custom_block_workflow_id_workflow_id_fk": { + "name": "custom_block_workflow_id_workflow_id_fk", + "tableFrom": "custom_block", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "custom_block_created_by_user_id_fk": { + "name": "custom_block_created_by_user_id_fk", + "tableFrom": "custom_block", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "custom_tools_workspace_id_workspace_id_fk": { + "name": "custom_tools_workspace_id_workspace_id_fk", + "tableFrom": "custom_tools", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "custom_tools_user_id_user_id_fk": { + "name": "custom_tools_user_id_user_id_fk", + "tableFrom": "custom_tools", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "dashboard_workspace_id_workspace_id_fk": { + "name": "dashboard_workspace_id_workspace_id_fk", + "tableFrom": "dashboard", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "dashboard_created_by_user_id_fk": { + "name": "dashboard_created_by_user_id_fk", + "tableFrom": "dashboard", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "dashboard_updated_by_user_id_fk": { + "name": "dashboard_updated_by_user_id_fk", + "tableFrom": "dashboard", + "columnsFrom": ["updated_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "data_drain_runs_drain_id_data_drains_id_fk": { + "name": "data_drain_runs_drain_id_data_drains_id_fk", + "tableFrom": "data_drain_runs", + "columnsFrom": ["drain_id"], + "tableTo": "data_drains", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "data_drains_organization_id_organization_id_fk": { + "name": "data_drains_organization_id_organization_id_fk", + "tableFrom": "data_drains", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "data_drains_created_by_user_id_fk": { + "name": "data_drains_created_by_user_id_fk", + "tableFrom": "data_drains", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "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": { + "type": "stored", + "as": "to_tsvector('english', \"docs_embeddings\".\"chunk_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": { + "docs_emb_source_document_idx": { + "name": "docs_emb_source_document_idx", + "columns": [ + { + "expression": "source_document", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "docs_emb_header_level_idx": { + "name": "docs_emb_header_level_idx", + "columns": [ + { + "expression": "header_level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "docs_emb_model_idx": { + "name": "docs_emb_model_idx", + "columns": [ + { + "expression": "embedding_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "docs_emb_created_at_idx": { + "name": "docs_emb_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": { + "m": 16, + "ef_construction": 64 + }, + "method": "hnsw", + "concurrently": false + }, + "docs_emb_metadata_gin_idx": { + "name": "docs_emb_metadata_gin_idx", + "columns": [ + { + "expression": "metadata", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "gin", + "concurrently": false + }, + "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, + "with": {}, + "method": "gin", + "concurrently": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"document\".\"deleted_at\" IS NULL", + "concurrently": true + }, + "doc_acl_gin_idx": { + "name": "doc_acl_gin_idx", + "columns": [ + { + "expression": "acl", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "array_ops" + } + ], + "isUnique": false, + "with": {}, + "method": "gin", + "where": "\"document\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "doc_filename_idx": { + "name": "doc_filename_idx", + "columns": [ + { + "expression": "filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "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 + }, + "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, + "with": {}, + "method": "btree", + "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 + }, + "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, + "with": {}, + "method": "btree", + "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 + }, + "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, + "with": {}, + "method": "btree", + "where": "\"document\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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)", + "isExpression": true, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL", + "concurrently": true + }, + "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, + "with": {}, + "method": "btree", + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "doc_storage_key_idx": { + "name": "doc_storage_key_idx", + "columns": [ + { + "expression": "storage_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"document\".\"storage_key\" IS NOT NULL", + "concurrently": false + }, + "doc_archived_at_partial_idx": { + "name": "doc_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"document\".\"archived_at\" IS NOT NULL", + "concurrently": false + }, + "doc_deleted_at_partial_idx": { + "name": "doc_deleted_at_partial_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"document\".\"deleted_at\" IS NOT NULL", + "concurrently": false + }, + "doc_connector_tombstone_idx": { + "name": "doc_connector_tombstone_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"document\".\"archived_at\" IS NULL AND (\"document\".\"deleted_at\" IS NOT NULL OR \"document\".\"content_hash\" IS NULL)", + "concurrently": false + }, + "doc_connector_live_idx": { + "name": "doc_connector_live_idx", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"document\".\"user_excluded\" = false AND \"document\".\"archived_at\" IS NULL AND \"document\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "doc_number1_idx": { + "name": "doc_number1_idx", + "columns": [ + { + "expression": "number1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "doc_number2_idx": { + "name": "doc_number2_idx", + "columns": [ + { + "expression": "number2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "doc_number3_idx": { + "name": "doc_number3_idx", + "columns": [ + { + "expression": "number3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "doc_number4_idx": { + "name": "doc_number4_idx", + "columns": [ + { + "expression": "number4", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "doc_number5_idx": { + "name": "doc_number5_idx", + "columns": [ + { + "expression": "number5", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "doc_date1_idx": { + "name": "doc_date1_idx", + "columns": [ + { + "expression": "date1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": true + }, + "doc_date2_idx": { + "name": "doc_date2_idx", + "columns": [ + { + "expression": "date2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": true + }, + "doc_boolean1_idx": { + "name": "doc_boolean1_idx", + "columns": [ + { + "expression": "boolean1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "doc_boolean2_idx": { + "name": "doc_boolean2_idx", + "columns": [ + { + "expression": "boolean2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "doc_boolean3_idx": { + "name": "doc_boolean3_idx", + "columns": [ + { + "expression": "boolean3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "document_knowledge_base_id_knowledge_base_id_fk": { + "name": "document_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "document", + "columnsFrom": ["knowledge_base_id"], + "tableTo": "knowledge_base", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "document_connector_id_knowledge_connector_id_fk": { + "name": "document_connector_id_knowledge_connector_id_fk", + "tableFrom": "document", + "columnsFrom": ["connector_id"], + "tableTo": "knowledge_connector", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "document_uploaded_by_user_id_fk": { + "name": "document_uploaded_by_user_id_fk", + "tableFrom": "document", + "columnsFrom": ["uploaded_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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", + "columnsFrom": ["document_id"], + "tableTo": "document", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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": { + "type": "stored", + "as": "to_tsvector('english', \"embedding\".\"content\")" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "emb_number1_idx": { + "name": "emb_number1_idx", + "columns": [ + { + "expression": "number1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "emb_number2_idx": { + "name": "emb_number2_idx", + "columns": [ + { + "expression": "number2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "emb_number3_idx": { + "name": "emb_number3_idx", + "columns": [ + { + "expression": "number3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "emb_number4_idx": { + "name": "emb_number4_idx", + "columns": [ + { + "expression": "number4", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "emb_number5_idx": { + "name": "emb_number5_idx", + "columns": [ + { + "expression": "number5", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "emb_date1_idx": { + "name": "emb_date1_idx", + "columns": [ + { + "expression": "date1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": true + }, + "emb_date2_idx": { + "name": "emb_date2_idx", + "columns": [ + { + "expression": "date2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": true + }, + "emb_boolean1_idx": { + "name": "emb_boolean1_idx", + "columns": [ + { + "expression": "boolean1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "emb_boolean2_idx": { + "name": "emb_boolean2_idx", + "columns": [ + { + "expression": "boolean2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "emb_boolean3_idx": { + "name": "emb_boolean3_idx", + "columns": [ + { + "expression": "boolean3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "emb_content_fts_idx": { + "name": "emb_content_fts_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "gin", + "concurrently": false + } + }, + "foreignKeys": { + "embedding_knowledge_base_id_knowledge_base_id_fk": { + "name": "embedding_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "embedding", + "columnsFrom": ["knowledge_base_id"], + "tableTo": "knowledge_base", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "embedding_document_id_document_id_fk": { + "name": "embedding_document_id_document_id_fk", + "tableFrom": "embedding", + "columnsFrom": ["document_id"], + "tableTo": "document", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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_search": { + "name": "embedding_keyword_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 + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "embedding_keyword_search_kb_idx": { + "name": "embedding_keyword_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "embedding_keyword_search_document_idx": { + "name": "embedding_keyword_search_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "embedding_keyword_search_content_idx": { + "name": "embedding_keyword_search_content_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "gin", + "concurrently": false + } + }, + "foreignKeys": { + "embedding_keyword_search_id_embedding_id_fk": { + "name": "embedding_keyword_search_id_embedding_id_fk", + "tableFrom": "embedding_keyword_search", + "columnsFrom": ["id"], + "tableTo": "embedding", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "embedding_keyword_tin_id_embedding_id_fk": { + "name": "embedding_keyword_tin_id_embedding_id_fk", + "tableFrom": "embedding_keyword_tin", + "columnsFrom": ["id"], + "tableTo": "embedding", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"embedding_search\".\"enabled\"", + "concurrently": true + }, + "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, + "with": { + "m": 16, + "ef_construction": 64 + }, + "method": "hnsw", + "concurrently": false + }, + "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, + "with": { + "m": 16, + "ef_construction": 64 + }, + "method": "hnsw", + "concurrently": false + }, + "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, + "with": { + "m": 16, + "ef_construction": 64 + }, + "method": "hnsw", + "concurrently": false + }, + "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, + "with": { + "m": 16, + "ef_construction": 64 + }, + "method": "hnsw", + "concurrently": false + }, + "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, + "with": { + "m": 16, + "ef_construction": 64 + }, + "method": "hnsw", + "concurrently": false + }, + "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, + "with": { + "m": 16, + "ef_construction": 64 + }, + "method": "hnsw", + "concurrently": false + } + }, + "foreignKeys": { + "embedding_search_id_embedding_id_fk": { + "name": "embedding_search_id_embedding_id_fk", + "tableFrom": "embedding_search", + "columnsFrom": ["id"], + "tableTo": "embedding", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["embedding_id"], + "tableTo": "embedding", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "environment_user_id_unique": { + "name": "environment_user_id_unique", + "columns": ["user_id"], + "nullsNotDistinct": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "execution_large_value_references_workflow_id_workflow_id_fk": { + "name": "execution_large_value_references_workflow_id_workflow_id_fk", + "tableFrom": "execution_large_value_references", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"execution_large_values\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"execution_large_values\".\"deleted_at\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "execution_large_values_workspace_id_workspace_id_fk": { + "name": "execution_large_values_workspace_id_workspace_id_fk", + "tableFrom": "execution_large_values", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "execution_large_values_workflow_id_workflow_id_fk": { + "name": "execution_large_values_workflow_id_workflow_id_fk", + "tableFrom": "execution_large_values", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "folder_deleted_at_idx": { + "name": "folder_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"folder\".\"deleted_at\" IS NOT NULL", + "concurrently": false + }, + "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\", '')", + "isExpression": true, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"folder\".\"deleted_at\" IS NULL", + "concurrently": false + } + }, + "foreignKeys": { + "folder_user_id_user_id_fk": { + "name": "folder_user_id_user_id_fk", + "tableFrom": "folder", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "folder_workspace_id_workspace_id_fk": { + "name": "folder_workspace_id_workspace_id_fk", + "tableFrom": "folder", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "folder_parent_id_folder_id_fk": { + "name": "folder_parent_id_folder_id_fk", + "tableFrom": "folder", + "columnsFrom": ["parent_id"], + "tableTo": "folder", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "invitation_organization_id_idx": { + "name": "invitation_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "invitation_status_idx": { + "name": "invitation_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"invitation\".\"status\" = 'pending' AND \"invitation\".\"organization_id\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "columnsFrom": ["inviter_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "invitation_token_unique": { + "name": "invitation_token_unique", + "columns": ["token"], + "nullsNotDistinct": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "invitation_workspace_grant_invitation_id_invitation_id_fk": { + "name": "invitation_workspace_grant_invitation_id_invitation_id_fk", + "tableFrom": "invitation_workspace_grant", + "columnsFrom": ["invitation_id"], + "tableTo": "invitation", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "invitation_workspace_grant_workspace_id_workspace_id_fk": { + "name": "invitation_workspace_grant_workspace_id_workspace_id_fk", + "tableFrom": "invitation_workspace_grant", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "job_execution_logs_trigger_idx": { + "name": "job_execution_logs_trigger_idx", + "columns": [ + { + "expression": "trigger", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "job_execution_logs_schedule_id_workflow_schedule_id_fk": { + "name": "job_execution_logs_schedule_id_workflow_schedule_id_fk", + "tableFrom": "job_execution_logs", + "columnsFrom": ["schedule_id"], + "tableTo": "workflow_schedule", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "job_execution_logs_workspace_id_workspace_id_fk": { + "name": "job_execution_logs_workspace_id_workspace_id_fk", + "tableFrom": "job_execution_logs", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "kb_organization_search_index_unique": { + "name": "kb_organization_search_index_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"knowledge_base\".\"is_search_index\" = true AND \"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "kb_user_id_idx": { + "name": "kb_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "kb_workspace_id_idx": { + "name": "kb_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "kb_folder_id_idx": { + "name": "kb_folder_id_idx", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "kb_deleted_at_idx": { + "name": "kb_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"knowledge_base\".\"deleted_at\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "kb_workspace_search_index_unique": { + "name": "kb_workspace_search_index_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"knowledge_base\".\"is_search_index\" = true AND \"knowledge_base\".\"deleted_at\" IS NULL", + "concurrently": false + } + }, + "foreignKeys": { + "knowledge_base_user_id_user_id_fk": { + "name": "knowledge_base_user_id_user_id_fk", + "tableFrom": "knowledge_base", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "knowledge_base_workspace_id_workspace_id_fk": { + "name": "knowledge_base_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_base", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "knowledge_base_organization_id_organization_id_fk": { + "name": "knowledge_base_organization_id_organization_id_fk", + "tableFrom": "knowledge_base", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "knowledge_base_folder_id_folder_id_fk": { + "name": "knowledge_base_folder_id_folder_id_fk", + "tableFrom": "knowledge_base", + "columnsFrom": ["folder_id"], + "tableTo": "folder", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["knowledge_base_id"], + "tableTo": "knowledge_base", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "kc_archived_at_partial_idx": { + "name": "kc_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"knowledge_connector\".\"archived_at\" IS NOT NULL", + "concurrently": false + }, + "kc_deleted_at_partial_idx": { + "name": "kc_deleted_at_partial_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"knowledge_connector\".\"deleted_at\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"knowledge_connector\".\"access_mode\" = 'members' AND \"knowledge_connector\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"knowledge_connector\".\"access_mode\" = 'admin' AND \"knowledge_connector\".\"deleted_at\" IS NULL", + "concurrently": false + } + }, + "foreignKeys": { + "knowledge_connector_knowledge_base_id_knowledge_base_id_fk": { + "name": "knowledge_connector_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "knowledge_connector", + "columnsFrom": ["knowledge_base_id"], + "tableTo": "knowledge_base", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "knowledge_connector_credential_group_id_credential_group_id_fk": { + "name": "knowledge_connector_credential_group_id_credential_group_id_fk", + "tableFrom": "knowledge_connector", + "columnsFrom": ["credential_group_id"], + "tableTo": "credential_group", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "kcm_credential_idx": { + "name": "kcm_credential_idx", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "knowledge_connector_member_workspace_id_workspace_id_fk": { + "name": "knowledge_connector_member_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_connector_member", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "knowledge_connector_member_organization_id_organization_id_fk": { + "name": "knowledge_connector_member_organization_id_organization_id_fk", + "tableFrom": "knowledge_connector_member", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "knowledge_connector_member_connector_id_knowledge_connector_id_fk": { + "name": "knowledge_connector_member_connector_id_knowledge_connector_id_fk", + "tableFrom": "knowledge_connector_member", + "columnsFrom": ["connector_id"], + "tableTo": "knowledge_connector", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "knowledge_connector_member_credential_id_credential_id_fk": { + "name": "knowledge_connector_member_credential_id_credential_id_fk", + "tableFrom": "knowledge_connector_member", + "columnsFrom": ["credential_id"], + "tableTo": "credential", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "kcmsl_started_at_partial_idx": { + "name": "kcmsl_started_at_partial_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"knowledge_connector_member_sync_log\".\"status\" = 'started'", + "concurrently": false + } + }, + "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", + "columnsFrom": ["connector_id"], + "tableTo": "knowledge_connector", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "knowledge_connector_partition_connector_id_knowledge_connector_id_fk": { + "name": "knowledge_connector_partition_connector_id_knowledge_connector_id_fk", + "tableFrom": "knowledge_connector_partition", + "columnsFrom": ["connector_id"], + "tableTo": "knowledge_connector", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "kcpg_snapshot_fk": { + "name": "kcpg_snapshot_fk", + "tableFrom": "knowledge_connector_permission_grant", + "columnsFrom": ["connector_id"], + "tableTo": "knowledge_connector_permission_snapshot", + "columnsTo": ["connector_id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["connector_id"], + "tableTo": "knowledge_connector", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "kcsl_started_at_partial_idx": { + "name": "kcsl_started_at_partial_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"knowledge_connector_sync_log\".\"status\" = 'started'", + "concurrently": false + } + }, + "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", + "columnsFrom": ["connector_id"], + "tableTo": "knowledge_connector", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "knowledge_document_observation_document_id_document_id_fk": { + "name": "knowledge_document_observation_document_id_document_id_fk", + "tableFrom": "knowledge_document_observation", + "columnsFrom": ["document_id"], + "tableTo": "document", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["member_id"], + "tableTo": "knowledge_connector_member", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "knowledge_external_directory_workspace_id_workspace_id_fk": { + "name": "knowledge_external_directory_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_external_directory", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "knowledge_external_directory_organization_id_organization_id_fk": { + "name": "knowledge_external_directory_organization_id_organization_id_fk", + "tableFrom": "knowledge_external_directory", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "knowledge_external_group_organization_id_organization_id_fk": { + "name": "knowledge_external_group_organization_id_organization_id_fk", + "tableFrom": "knowledge_external_group", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "keg_workspace_fk": { + "name": "keg_workspace_fk", + "tableFrom": "knowledge_external_group", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "kegm_group_fk": { + "name": "kegm_group_fk", + "tableFrom": "knowledge_external_group_member", + "columnsFrom": ["group_id"], + "tableTo": "knowledge_external_group", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "knowledge_projection_dirty_document_id_document_id_fk": { + "name": "knowledge_projection_dirty_document_id_document_id_fk", + "tableFrom": "knowledge_projection_dirty", + "columnsFrom": ["document_id"], + "tableTo": "document", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "mcp_server_oauth_state_idx": { + "name": "mcp_server_oauth_state_idx", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["mcp_server_id"], + "tableTo": "mcp_servers", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mcp_server_oauth_user_id_user_id_fk": { + "name": "mcp_server_oauth_user_id_user_id_fk", + "tableFrom": "mcp_server_oauth", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "mcp_server_oauth_workspace_id_workspace_id_fk": { + "name": "mcp_server_oauth_workspace_id_workspace_id_fk", + "tableFrom": "mcp_server_oauth", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mcp_server_oauth_organization_id_organization_id_fk": { + "name": "mcp_server_oauth_organization_id_organization_id_fk", + "tableFrom": "mcp_server_oauth", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "mcp_servers_credential_group_idx": { + "name": "mcp_servers_credential_group_idx", + "columns": [ + { + "expression": "credential_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "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 + }, + "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, + "with": {}, + "method": "btree", + "where": "\"mcp_servers\".\"deleted_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "mcp_servers_workspace_id_workspace_id_fk": { + "name": "mcp_servers_workspace_id_workspace_id_fk", + "tableFrom": "mcp_servers", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mcp_servers_organization_id_organization_id_fk": { + "name": "mcp_servers_organization_id_organization_id_fk", + "tableFrom": "mcp_servers", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mcp_servers_credential_group_id_credential_group_id_fk": { + "name": "mcp_servers_credential_group_id_credential_group_id_fk", + "tableFrom": "mcp_servers", + "columnsFrom": ["credential_group_id"], + "tableTo": "credential_group", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "mcp_servers_created_by_user_id_fk": { + "name": "mcp_servers_created_by_user_id_fk", + "tableFrom": "mcp_servers", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "member_organization_id_idx": { + "name": "member_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "memory_workspace_idx": { + "name": "memory_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"memory\".\"deleted_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "memory_workspace_id_workspace_id_fk": { + "name": "memory_workspace_id_workspace_id_fk", + "tableFrom": "memory", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "memory_artifact_memory_id_memory_id_fk": { + "name": "memory_artifact_memory_id_memory_id_fk", + "tableFrom": "memory_artifact", + "columnsFrom": ["memory_id"], + "tableTo": "memory", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "memory_artifact_key_execution_large_values_key_fk": { + "name": "memory_artifact_key_execution_large_values_key_fk", + "tableFrom": "memory_artifact", + "columnsFrom": ["key"], + "tableTo": "execution_large_values", + "columnsTo": ["key"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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": { + "name": "memory_item_sequence_seq", + "increment": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "always" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "memory_item_memory_id_memory_id_fk": { + "name": "memory_item_memory_id_memory_id_fk", + "tableFrom": "memory_item", + "columnsFrom": ["memory_id"], + "tableTo": "memory", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["memory_id"], + "tableTo": "memory", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mothership_inbox_allowed_sender_added_by_user_id_fk": { + "name": "mothership_inbox_allowed_sender_added_by_user_id_fk", + "tableFrom": "mothership_inbox_allowed_sender", + "columnsFrom": ["added_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "mothership_inbox_task_workspace_id_workspace_id_fk": { + "name": "mothership_inbox_task_workspace_id_workspace_id_fk", + "tableFrom": "mothership_inbox_task", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mothership_inbox_task_chat_id_copilot_chats_id_fk": { + "name": "mothership_inbox_task_chat_id_copilot_chats_id_fk", + "tableFrom": "mothership_inbox_task", + "columnsFrom": ["chat_id"], + "tableTo": "copilot_chats", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mothership_inbox_webhook_workspace_id_unique": { + "name": "mothership_inbox_webhook_workspace_id_unique", + "columns": ["workspace_id"], + "nullsNotDistinct": false + } + }, + "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", + "columnsFrom": ["chat_id"], + "tableTo": "copilot_chats", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["client_id"], + "tableTo": "oauth_client", + "columnsTo": ["client_id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_access_token_session_id_session_id_fk": { + "name": "oauth_access_token_session_id_session_id_fk", + "tableFrom": "oauth_access_token", + "columnsFrom": ["session_id"], + "tableTo": "session", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "oauth_access_token_user_id_user_id_fk": { + "name": "oauth_access_token_user_id_user_id_fk", + "tableFrom": "oauth_access_token", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["refresh_id"], + "tableTo": "oauth_refresh_token", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_access_token_token_unique": { + "name": "oauth_access_token_token_unique", + "columns": ["token"], + "nullsNotDistinct": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "oauth_client_user_id_user_id_fk": { + "name": "oauth_client_user_id_user_id_fk", + "tableFrom": "oauth_client", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_client_client_id_unique": { + "name": "oauth_client_client_id_unique", + "columns": ["client_id"], + "nullsNotDistinct": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "oauth_consent_client_id_oauth_client_client_id_fk": { + "name": "oauth_consent_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_consent", + "columnsFrom": ["client_id"], + "tableTo": "oauth_client", + "columnsTo": ["client_id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_consent_user_id_user_id_fk": { + "name": "oauth_consent_user_id_user_id_fk", + "tableFrom": "oauth_consent", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_consent_user_client_reference_unique": { + "name": "oauth_consent_user_client_reference_unique", + "columns": ["user_id", "client_id", "reference_id"], + "nullsNotDistinct": true + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["client_id"], + "tableTo": "oauth_client", + "columnsTo": ["client_id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_refresh_token_session_id_session_id_fk": { + "name": "oauth_refresh_token_session_id_session_id_fk", + "tableFrom": "oauth_refresh_token", + "columnsFrom": ["session_id"], + "tableTo": "session", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "oauth_refresh_token_user_id_user_id_fk": { + "name": "oauth_refresh_token_user_id_user_id_fk", + "tableFrom": "oauth_refresh_token", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["family_id"], + "tableTo": "oauth_token_family", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_refresh_token_token_unique": { + "name": "oauth_refresh_token_token_unique", + "columns": ["token"], + "nullsNotDistinct": false + }, + "oauth_refresh_token_family_generation_unique": { + "name": "oauth_refresh_token_family_generation_unique", + "columns": ["family_id", "generation"], + "nullsNotDistinct": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["client_id"], + "tableTo": "oauth_client", + "columnsTo": ["client_id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_token_family_session_id_session_id_fk": { + "name": "oauth_token_family_session_id_session_id_fk", + "tableFrom": "oauth_token_family", + "columnsFrom": ["session_id"], + "tableTo": "session", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "oauth_token_family_user_id_user_id_fk": { + "name": "oauth_token_family_user_id_user_id_fk", + "tableFrom": "oauth_token_family", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "oauth_token_family_consent_id_oauth_consent_id_fk": { + "name": "oauth_token_family_consent_id_oauth_consent_id_fk", + "tableFrom": "oauth_token_family", + "columnsFrom": ["consent_id"], + "tableTo": "oauth_consent", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "organization_access_request_settings_updated_by_user_id_fk": { + "name": "organization_access_request_settings_updated_by_user_id_fk", + "tableFrom": "organization_access_request_settings", + "columnsFrom": ["updated_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "organization_byok_keys_organization_id_organization_id_fk": { + "name": "organization_byok_keys_organization_id_organization_id_fk", + "tableFrom": "organization_byok_keys", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "organization_byok_keys_created_by_user_id_fk": { + "name": "organization_byok_keys_created_by_user_id_fk", + "tableFrom": "organization_byok_keys", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "organization_member_usage_limit_user_id_user_id_fk": { + "name": "organization_member_usage_limit_user_id_user_id_fk", + "tableFrom": "organization_member_usage_limit", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "organization_member_usage_limit_set_by_user_id_fk": { + "name": "organization_member_usage_limit_set_by_user_id_fk", + "tableFrom": "organization_member_usage_limit", + "columnsFrom": ["set_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "organization_search_history_organization_id_organization_id_fk": { + "name": "organization_search_history_organization_id_organization_id_fk", + "tableFrom": "organization_search_history", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "organization_search_history_user_id_user_id_fk": { + "name": "organization_search_history_user_id_user_id_fk", + "tableFrom": "organization_search_history", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "organization_search_invocation_user_idx": { + "name": "organization_search_invocation_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "organization_search_invocation_organization_id_organization_id_fk": { + "name": "organization_search_invocation_organization_id_organization_id_fk", + "tableFrom": "organization_search_invocation", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "organization_search_invocation_user_id_user_id_fk": { + "name": "organization_search_invocation_user_id_user_id_fk", + "tableFrom": "organization_search_invocation", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "org_search_mcp_invocation_org_fk": { + "name": "org_search_mcp_invocation_org_fk", + "tableFrom": "organization_search_mcp_invocation", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "org_search_mcp_invocation_user_fk": { + "name": "org_search_mcp_invocation_user_fk", + "tableFrom": "organization_search_mcp_invocation", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "where": "\"organization_secret\".\"owner_user_id\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"organization_secret\".\"owner_user_id\" IS NOT NULL", + "concurrently": false + }, + "organization_secret_owner_idx": { + "name": "organization_secret_owner_idx", + "columns": [ + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "organization_secret_source_id_organization_secret_source_id_fk": { + "name": "organization_secret_source_id_organization_secret_source_id_fk", + "tableFrom": "organization_secret", + "columnsFrom": ["source_id"], + "tableTo": "organization_secret_source", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "organization_secret_owner_user_id_user_id_fk": { + "name": "organization_secret_owner_user_id_user_id_fk", + "tableFrom": "organization_secret", + "columnsFrom": ["owner_user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "organization_secret_source_organization_id_organization_id_fk": { + "name": "organization_secret_source_organization_id_organization_id_fk", + "tableFrom": "organization_secret_source", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"outbox_event\".\"status\" = 'pending'", + "concurrently": false + }, + "outbox_event_locked_at_idx": { + "name": "outbox_event_locked_at_idx", + "columns": [ + { + "expression": "locked_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "paused_executions_status_idx": { + "name": "paused_executions_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "paused_executions_execution_id_unique": { + "name": "paused_executions_execution_id_unique", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "status = 'paused' AND next_resume_at IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "paused_executions_workflow_id_workflow_id_fk": { + "name": "paused_executions_workflow_id_workflow_id_fk", + "tableFrom": "paused_executions", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "pending_credential_draft_user_id_user_id_fk": { + "name": "pending_credential_draft_user_id_user_id_fk", + "tableFrom": "pending_credential_draft", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "pending_credential_draft_workspace_id_workspace_id_fk": { + "name": "pending_credential_draft_workspace_id_workspace_id_fk", + "tableFrom": "pending_credential_draft", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "pending_credential_draft_organization_id_organization_id_fk": { + "name": "pending_credential_draft_organization_id_organization_id_fk", + "tableFrom": "pending_credential_draft", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "pending_credential_draft_credential_id_credential_id_fk": { + "name": "pending_credential_draft_credential_id_credential_id_fk", + "tableFrom": "pending_credential_draft", + "columnsFrom": ["credential_id"], + "tableTo": "credential", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "where": "\"permission_access_request\".\"status\" = 'pending'", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "permission_access_request_organization_id_organization_id_fk": { + "name": "permission_access_request_organization_id_organization_id_fk", + "tableFrom": "permission_access_request", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "permission_access_request_requester_id_user_id_fk": { + "name": "permission_access_request_requester_id_user_id_fk", + "tableFrom": "permission_access_request", + "columnsFrom": ["requester_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "permission_access_request_decided_by_user_id_fk": { + "name": "permission_access_request_decided_by_user_id_fk", + "tableFrom": "permission_access_request", + "columnsFrom": ["decided_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "permission_group_organization_default_unique": { + "name": "permission_group_organization_default_unique", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "is_default = true", + "concurrently": false + } + }, + "foreignKeys": { + "permission_group_organization_id_organization_id_fk": { + "name": "permission_group_organization_id_organization_id_fk", + "tableFrom": "permission_group", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "permission_group_created_by_user_id_fk": { + "name": "permission_group_created_by_user_id_fk", + "tableFrom": "permission_group", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["permission_group_id"], + "tableTo": "permission_group", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "permission_group_member_organization_id_organization_id_fk": { + "name": "permission_group_member_organization_id_organization_id_fk", + "tableFrom": "permission_group_member", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "permission_group_member_user_id_user_id_fk": { + "name": "permission_group_member_user_id_user_id_fk", + "tableFrom": "permission_group_member", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "permission_group_member_assigned_by_user_id_fk": { + "name": "permission_group_member_assigned_by_user_id_fk", + "tableFrom": "permission_group_member", + "columnsFrom": ["assigned_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["permission_group_id"], + "tableTo": "permission_group", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "permission_group_workspace_workspace_id_workspace_id_fk": { + "name": "permission_group_workspace_workspace_id_workspace_id_fk", + "tableFrom": "permission_group_workspace", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "permission_group_workspace_organization_id_organization_id_fk": { + "name": "permission_group_workspace_organization_id_organization_id_fk", + "tableFrom": "permission_group_workspace", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "permissions_user_id_user_id_fk": { + "name": "permissions_user_id_user_id_fk", + "tableFrom": "permissions", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "pinned_item_user_id_user_id_fk": { + "name": "pinned_item_user_id_user_id_fk", + "tableFrom": "pinned_item", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "pinned_item_workspace_id_workspace_id_fk": { + "name": "pinned_item_workspace_id_workspace_id_fk", + "tableFrom": "pinned_item", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "project_organization_id_organization_id_fk": { + "name": "project_organization_id_organization_id_fk", + "tableFrom": "project", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "project_owner_id_user_id_fk": { + "name": "project_owner_id_user_id_fk", + "tableFrom": "project", + "columnsFrom": ["owner_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "restrict" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "project_workspace_project_id_project_id_fk": { + "name": "project_workspace_project_id_project_id_fk", + "tableFrom": "project_workspace", + "columnsFrom": ["project_id"], + "tableTo": "project", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "restrict" + }, + "project_workspace_workspace_id_workspace_id_fk": { + "name": "project_workspace_workspace_id_workspace_id_fk", + "tableFrom": "project_workspace", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "public_share_resource_id_idx": { + "name": "public_share_resource_id_idx", + "columns": [ + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "public_share_workspace_id_idx": { + "name": "public_share_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "public_share_workspace_id_workspace_id_fk": { + "name": "public_share_workspace_id_workspace_id_fk", + "tableFrom": "public_share", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "public_share_created_by_user_id_fk": { + "name": "public_share_created_by_user_id_fk", + "tableFrom": "public_share", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "resource_policy_workspace_id_idx": { + "name": "resource_policy_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "resource_policy_workspace_id_workspace_id_fk": { + "name": "resource_policy_workspace_id_workspace_id_fk", + "tableFrom": "resource_policy", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "resource_policy_organization_id_organization_id_fk": { + "name": "resource_policy_organization_id_organization_id_fk", + "tableFrom": "resource_policy", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "resource_policy_created_by_user_id_fk": { + "name": "resource_policy_created_by_user_id_fk", + "tableFrom": "resource_policy", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "resource_policy_updated_by_user_id_fk": { + "name": "resource_policy_updated_by_user_id_fk", + "tableFrom": "resource_policy", + "columnsFrom": ["updated_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "resume_queue_new_execution_idx": { + "name": "resume_queue_new_execution_idx", + "columns": [ + { + "expression": "new_execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "resume_queue_paused_execution_id_paused_executions_id_fk": { + "name": "resume_queue_paused_execution_id_paused_executions_id_fk", + "tableFrom": "resume_queue", + "columnsFrom": ["paused_execution_id"], + "tableTo": "paused_executions", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sandbox_image_status_idx": { + "name": "sandbox_image_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sandbox_image_last_used_idx": { + "name": "sandbox_image_last_used_idx", + "columns": [ + { + "expression": "last_used_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "scim_connection_reconcile_due_idx": { + "name": "scim_connection_reconcile_due_idx", + "columns": [ + { + "expression": "reconciled_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "scim_connection_organization_id_organization_id_fk": { + "name": "scim_connection_organization_id_organization_id_fk", + "tableFrom": "scim_connection", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "scim_connection_created_by_user_id_fk": { + "name": "scim_connection_created_by_user_id_fk", + "tableFrom": "scim_connection", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "scim_credential_connection_idx": { + "name": "scim_credential_connection_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "scim_credential_connection_id_scim_connection_id_fk": { + "name": "scim_credential_connection_id_scim_connection_id_fk", + "tableFrom": "scim_credential", + "columnsFrom": ["connection_id"], + "tableTo": "scim_connection", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "scim_credential_revoked_by_user_id_fk": { + "name": "scim_credential_revoked_by_user_id_fk", + "tableFrom": "scim_credential", + "columnsFrom": ["revoked_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "scim_credential_created_by_user_id_fk": { + "name": "scim_credential_created_by_user_id_fk", + "tableFrom": "scim_credential", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "external_id is not null", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "scim_group_connection_id_scim_connection_id_fk": { + "name": "scim_group_connection_id_scim_connection_id_fk", + "tableFrom": "scim_group", + "columnsFrom": ["connection_id"], + "tableTo": "scim_connection", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "scim_group_mapping_workspace_idx": { + "name": "scim_group_mapping_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "scim_group_mapping_group_id_scim_group_id_fk": { + "name": "scim_group_mapping_group_id_scim_group_id_fk", + "tableFrom": "scim_group_mapping", + "columnsFrom": ["group_id"], + "tableTo": "scim_group", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["permission_group_id"], + "tableTo": "permission_group", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "scim_group_mapping_workspace_id_workspace_id_fk": { + "name": "scim_group_mapping_workspace_id_workspace_id_fk", + "tableFrom": "scim_group_mapping", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "scim_group_mapping_created_by_user_id_fk": { + "name": "scim_group_mapping_created_by_user_id_fk", + "tableFrom": "scim_group_mapping", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "scim_group_member_group_id_scim_group_id_fk": { + "name": "scim_group_member_group_id_scim_group_id_fk", + "tableFrom": "scim_group_member", + "columnsFrom": ["group_id"], + "tableTo": "scim_group", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["scim_user_id"], + "tableTo": "scim_user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "scim_projection_grant_connection_idx": { + "name": "scim_projection_grant_connection_idx", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "scim_projection_grant_connection_id_scim_connection_id_fk": { + "name": "scim_projection_grant_connection_id_scim_connection_id_fk", + "tableFrom": "scim_projection_grant", + "columnsFrom": ["connection_id"], + "tableTo": "scim_connection", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["scim_user_id"], + "tableTo": "scim_user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "scim_request_log_connection_id_scim_connection_id_fk": { + "name": "scim_request_log_connection_id_scim_connection_id_fk", + "tableFrom": "scim_request_log", + "columnsFrom": ["connection_id"], + "tableTo": "scim_connection", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "external_id is not null", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "scim_user_user_idx": { + "name": "scim_user_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "scim_user_connection_id_scim_connection_id_fk": { + "name": "scim_user_connection_id_scim_connection_id_fk", + "tableFrom": "scim_user", + "columnsFrom": ["connection_id"], + "tableTo": "scim_connection", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "scim_user_user_id_user_id_fk": { + "name": "scim_user_user_id_user_id_fk", + "tableFrom": "scim_user", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "scim_user_tombstone_user_idx": { + "name": "scim_user_tombstone_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "scim_user_tombstone_connection_id_scim_connection_id_fk": { + "name": "scim_user_tombstone_connection_id_scim_connection_id_fk", + "tableFrom": "scim_user_tombstone", + "columnsFrom": ["connection_id"], + "tableTo": "scim_connection", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "scim_user_tombstone_user_id_user_id_fk": { + "name": "scim_user_tombstone_user_id_user_id_fk", + "tableFrom": "scim_user_tombstone", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "secret_usage_workspace_id_workspace_id_fk": { + "name": "secret_usage_workspace_id_workspace_id_fk", + "tableFrom": "secret_usage", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "session_active_organization_id_organization_id_fk": { + "name": "session_active_organization_id_organization_id_fk", + "tableFrom": "session", + "columnsFrom": ["active_organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "columns": ["token"], + "nullsNotDistinct": false + } + }, + "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", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "settings_user_id_unique": { + "name": "settings_user_id_unique", + "columns": ["user_id"], + "nullsNotDistinct": false + } + }, + "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", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "skill_workspace_id_workspace_id_fk": { + "name": "skill_workspace_id_workspace_id_fk", + "tableFrom": "skill", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "skill_user_id_user_id_fk": { + "name": "skill_user_id_user_id_fk", + "tableFrom": "skill", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "skill_member_skill_id_skill_id_fk": { + "name": "skill_member_skill_id_skill_id_fk", + "tableFrom": "skill_member", + "columnsFrom": ["skill_id"], + "tableTo": "skill", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "skill_member_user_id_user_id_fk": { + "name": "skill_member_user_id_user_id_fk", + "tableFrom": "skill_member", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "skill_member_invited_by_user_id_fk": { + "name": "skill_member_invited_by_user_id_fk", + "tableFrom": "skill_member", + "columnsFrom": ["invited_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "slack_search_installation_credential_unique": { + "name": "slack_search_installation_credential_unique", + "columns": [ + { + "expression": "credential_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"slack_search_installation\".\"enabled\" = true", + "concurrently": false + } + }, + "foreignKeys": { + "slack_search_installation_organization_id_organization_id_fk": { + "name": "slack_search_installation_organization_id_organization_id_fk", + "tableFrom": "slack_search_installation", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "slack_search_installation_credential_id_credential_id_fk": { + "name": "slack_search_installation_credential_id_credential_id_fk", + "tableFrom": "slack_search_installation", + "columnsFrom": ["credential_id"], + "tableTo": "credential", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["slack_app_id"], + "tableTo": "slack_app", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "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": { + "name": "slack_search_turn_ordinal_seq", + "increment": "1", + "minValue": "1", + "maxValue": "2147483647", + "startWith": "1", + "cache": "1", + "cycle": false, + "schema": "public", + "type": "always" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"slack_search_turn\".\"status\" = 'running'", + "concurrently": false + } + }, + "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", + "columnsFrom": ["installation_id"], + "tableTo": "slack_search_installation", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sso_domain_domain_idx": { + "name": "sso_domain_domain_idx", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sso_domain_verified_unique": { + "name": "sso_domain_verified_unique", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "status = 'verified'", + "concurrently": false + } + }, + "foreignKeys": { + "sso_domain_organization_id_organization_id_fk": { + "name": "sso_domain_organization_id_organization_id_fk", + "tableFrom": "sso_domain", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "sso_domain_created_by_user_id_fk": { + "name": "sso_domain_created_by_user_id_fk", + "tableFrom": "sso_domain", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sso_provider_domain_idx": { + "name": "sso_provider_domain_idx", + "columns": [ + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sso_provider_user_id_idx": { + "name": "sso_provider_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sso_provider_organization_id_idx": { + "name": "sso_provider_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "sso_provider_user_id_user_id_fk": { + "name": "sso_provider_user_id_user_id_fk", + "tableFrom": "sso_provider", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "sso_provider_organization_id_organization_id_fk": { + "name": "sso_provider_organization_id_organization_id_fk", + "tableFrom": "sso_provider", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "subscription_cycle_close_lagging_idx": { + "name": "subscription_cycle_close_lagging_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "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 + } + }, + "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, + "with": {}, + "method": "btree", + "where": "\"table_jobs\".\"status\" = 'running' AND \"table_jobs\".\"type\" <> 'export'", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "table_jobs_table_id_user_table_definitions_id_fk": { + "name": "table_jobs_table_id_user_table_definitions_id_fk", + "tableFrom": "table_jobs", + "columnsFrom": ["table_id"], + "tableTo": "user_table_definitions", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "table_jobs_workspace_id_workspace_id_fk": { + "name": "table_jobs_workspace_id_workspace_id_fk", + "tableFrom": "table_jobs", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "where": "\"table_row_executions\".\"status\" IN ('queued', 'running', 'pending')", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"table_row_executions\".\"execution_id\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["table_id"], + "tableTo": "user_table_definitions", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["row_id"], + "tableTo": "user_table_rows", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["capability_governed_user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"table_run_dispatches\".\"status\" IN ('pending', 'dispatching')", + "concurrently": false + } + }, + "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", + "columnsFrom": ["table_id"], + "tableTo": "user_table_definitions", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "table_run_dispatches_workspace_id_workspace_id_fk": { + "name": "table_run_dispatches_workspace_id_workspace_id_fk", + "tableFrom": "table_run_dispatches", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["triggered_by_user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "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", + "columnsFrom": ["capability_governed_user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "table_views_table_default_unique": { + "name": "table_views_table_default_unique", + "columns": [ + { + "expression": "table_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "is_default = true", + "concurrently": false + } + }, + "foreignKeys": { + "table_views_table_id_user_table_definitions_id_fk": { + "name": "table_views_table_id_user_table_definitions_id_fk", + "tableFrom": "table_views", + "columnsFrom": ["table_id"], + "tableTo": "user_table_definitions", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "table_views_workspace_id_workspace_id_fk": { + "name": "table_views_workspace_id_workspace_id_fk", + "tableFrom": "table_views", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "table_views_created_by_user_id_fk": { + "name": "table_views_created_by_user_id_fk", + "tableFrom": "table_views", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "upload_session_final_key_unique": { + "name": "upload_session_final_key_unique", + "columns": [ + { + "expression": "final_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "usage_log_source_idx": { + "name": "usage_log_source_idx", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "usage_log_workflow_id_idx": { + "name": "usage_log_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "usage_log_event_key_unique": { + "name": "usage_log_event_key_unique", + "columns": [ + { + "expression": "event_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"usage_log\".\"event_key\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"usage_log\".\"billing_entity_type\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"usage_log\".\"billing_entity_type\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"usage_log\".\"billing_entity_type\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "usage_log_execution_id_idx": { + "name": "usage_log_execution_id_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "usage_log_user_id_user_id_fk": { + "name": "usage_log_user_id_user_id_fk", + "tableFrom": "usage_log", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "usage_log_workspace_id_workspace_id_fk": { + "name": "usage_log_workspace_id_workspace_id_fk", + "tableFrom": "usage_log", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "usage_log_workflow_id_workflow_id_fk": { + "name": "usage_log_workflow_id_workflow_id_fk", + "tableFrom": "usage_log", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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\"))", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "columns": ["email"], + "nullsNotDistinct": false + }, + "user_normalized_email_unique": { + "name": "user_normalized_email_unique", + "columns": ["normalized_email"], + "nullsNotDistinct": false + } + }, + "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", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_stats_user_id_unique": { + "name": "user_stats_user_id_unique", + "columns": ["user_id"], + "nullsNotDistinct": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"user_table_definitions\".\"archived_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"user_table_definitions\".\"archived_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "user_table_definitions_workspace_id_workspace_id_fk": { + "name": "user_table_definitions_workspace_id_workspace_id_fk", + "tableFrom": "user_table_definitions", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_table_definitions_folder_id_folder_id_fk": { + "name": "user_table_definitions_folder_id_folder_id_fk", + "tableFrom": "user_table_definitions", + "columnsFrom": ["folder_id"], + "tableTo": "folder", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "user_table_definitions_created_by_user_id_fk": { + "name": "user_table_definitions_created_by_user_id_fk", + "tableFrom": "user_table_definitions", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["row_id"], + "tableTo": "user_table_rows", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "gin", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["table_id"], + "tableTo": "user_table_definitions", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_table_rows_workspace_id_workspace_id_fk": { + "name": "user_table_rows_workspace_id_workspace_id_fk", + "tableFrom": "user_table_rows", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_table_rows_created_by_user_id_fk": { + "name": "user_table_rows_created_by_user_id_fk", + "tableFrom": "user_table_rows", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "verification_expires_at_idx": { + "name": "verification_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columns": ["email"], + "nullsNotDistinct": false + } + }, + "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, + "with": {}, + "method": "btree", + "where": "\"webhook\".\"archived_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"webhook\".\"archived_at\" IS NULL AND \"webhook\".\"routing_key\" IS NOT NULL", + "concurrently": false + }, + "webhook_archived_at_partial_idx": { + "name": "webhook_archived_at_partial_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"webhook\".\"archived_at\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"webhook\".\"registration_status\" = 'active' AND \"webhook\".\"block_id\" IS NOT NULL AND \"webhook\".\"archived_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"webhook\".\"registration_status\" = 'candidate' AND \"webhook\".\"block_id\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "webhook_workflow_id_workflow_id_fk": { + "name": "webhook_workflow_id_workflow_id_fk", + "tableFrom": "webhook", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "webhook_deployment_version_id_workflow_deployment_version_id_fk": { + "name": "webhook_deployment_version_id_workflow_deployment_version_id_fk", + "tableFrom": "webhook", + "columnsFrom": ["deployment_version_id"], + "tableTo": "workflow_deployment_version", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "webhook_path_claim_workflow_id_workflow_id_fk": { + "name": "webhook_path_claim_workflow_id_workflow_id_fk", + "tableFrom": "webhook_path_claim", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workflow_workspace_id_idx": { + "name": "workflow_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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\", '')", + "isExpression": true, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"workflow\".\"archived_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow\".\"archived_at\" IS NULL", + "concurrently": false + }, + "workflow_archived_at_idx": { + "name": "workflow_archived_at_idx", + "columns": [ + { + "expression": "archived_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow\".\"archived_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_user_id_user_id_fk": { + "name": "workflow_user_id_user_id_fk", + "tableFrom": "workflow", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_workspace_id_workspace_id_fk": { + "name": "workflow_workspace_id_workspace_id_fk", + "tableFrom": "workflow", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_folder_id_folder_id_fk": { + "name": "workflow_folder_id_folder_id_fk", + "tableFrom": "workflow", + "columnsFrom": ["folder_id"], + "tableTo": "folder", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workflow_blocks_type_idx": { + "name": "workflow_blocks_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_blocks_workflow_id_workflow_id_fk": { + "name": "workflow_blocks_workflow_id_workflow_id_fk", + "tableFrom": "workflow_blocks", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workflow_checkpoints_workflow_id_idx": { + "name": "workflow_checkpoints_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workflow_checkpoints_chat_id_idx": { + "name": "workflow_checkpoints_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workflow_checkpoints_message_id_idx": { + "name": "workflow_checkpoints_message_id_idx", + "columns": [ + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workflow_checkpoints_created_at_idx": { + "name": "workflow_checkpoints_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_checkpoints_user_id_user_id_fk": { + "name": "workflow_checkpoints_user_id_user_id_fk", + "tableFrom": "workflow_checkpoints", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_checkpoints_workflow_id_workflow_id_fk": { + "name": "workflow_checkpoints_workflow_id_workflow_id_fk", + "tableFrom": "workflow_checkpoints", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_checkpoints_chat_id_copilot_chats_id_fk": { + "name": "workflow_checkpoints_chat_id_copilot_chats_id_fk", + "tableFrom": "workflow_checkpoints", + "columnsFrom": ["chat_id"], + "tableTo": "copilot_chats", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_deployment_operation\".\"idempotency_key\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_deployment_operation\".\"status\" IN ('preparing', 'activating')", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_deployment_operation_workflow_id_workflow_id_fk": { + "name": "workflow_deployment_operation_workflow_id_workflow_id_fk", + "tableFrom": "workflow_deployment_operation", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["deployment_version_id"], + "tableTo": "workflow_deployment_version", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["previous_active_version_id"], + "tableTo": "workflow_deployment_version", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_deployment_version_workflow_id_workflow_id_fk": { + "name": "workflow_deployment_version_workflow_id_workflow_id_fk", + "tableFrom": "workflow_deployment_version", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_edges_workflow_id_workflow_id_fk": { + "name": "workflow_edges_workflow_id_workflow_id_fk", + "tableFrom": "workflow_edges", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_edges_source_block_id_workflow_blocks_id_fk": { + "name": "workflow_edges_source_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_edges", + "columnsFrom": ["source_block_id"], + "tableTo": "workflow_blocks", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_edges_target_block_id_workflow_blocks_id_fk": { + "name": "workflow_edges_target_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_edges", + "columnsFrom": ["target_block_id"], + "tableTo": "workflow_blocks", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workflow_execution_logs_trigger_idx": { + "name": "workflow_execution_logs_trigger_idx", + "columns": [ + { + "expression": "trigger", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workflow_execution_logs_level_idx": { + "name": "workflow_execution_logs_level_idx", + "columns": [ + { + "expression": "level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": true + }, + "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", + "isExpression": true, + "asc": true, + "nulls": "last" + }, + { + "expression": "\"id\" DESC", + "isExpression": true, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "gin", + "concurrently": false + }, + "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\")", + "isExpression": true, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "status = 'running'", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_execution_logs\".\"status\" = 'running' AND \"workflow_execution_logs\".\"execution_deadline_at\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "status = 'redacting'", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_execution_logs\".\"status\" = 'redacting' AND \"workflow_execution_logs\".\"execution_deadline_at\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_execution_logs\".\"status\" = 'completed' AND \"workflow_execution_logs\".\"level\" = 'info' AND \"workflow_execution_logs\".\"ended_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_execution_logs_workflow_id_workflow_id_fk": { + "name": "workflow_execution_logs_workflow_id_workflow_id_fk", + "tableFrom": "workflow_execution_logs", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "workflow_execution_logs_workspace_id_workspace_id_fk": { + "name": "workflow_execution_logs_workspace_id_workspace_id_fk", + "tableFrom": "workflow_execution_logs", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["state_snapshot_id"], + "tableTo": "workflow_execution_snapshots", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "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", + "columnsFrom": ["deployment_version_id"], + "tableTo": "workflow_deployment_version", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workflow_snapshots_hash_idx": { + "name": "workflow_snapshots_hash_idx", + "columns": [ + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workflow_snapshots_created_at_idx": { + "name": "workflow_snapshots_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_execution_snapshots_workflow_id_workflow_id_fk": { + "name": "workflow_execution_snapshots_workflow_id_workflow_id_fk", + "tableFrom": "workflow_execution_snapshots", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_mcp_server\".\"deleted_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_mcp_server_workspace_id_workspace_id_fk": { + "name": "workflow_mcp_server_workspace_id_workspace_id_fk", + "tableFrom": "workflow_mcp_server", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_mcp_server_created_by_user_id_fk": { + "name": "workflow_mcp_server_created_by_user_id_fk", + "tableFrom": "workflow_mcp_server", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_mcp_tool\".\"archived_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_mcp_tool\".\"archived_at\" IS NOT NULL", + "concurrently": false + } + }, + "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", + "columnsFrom": ["server_id"], + "tableTo": "workflow_mcp_server", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_mcp_tool_workflow_id_workflow_id_fk": { + "name": "workflow_mcp_tool_workflow_id_workflow_id_fk", + "tableFrom": "workflow_mcp_tool", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_schedule\".\"archived_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_schedule\".\"archived_at\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "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 + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workflow_schedule\".\"archived_at\" IS NULL AND \"workflow_schedule\".\"status\" NOT IN ('disabled', 'completed') AND \"workflow_schedule\".\"source_type\" = 'job'", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_schedule_workflow_id_workflow_id_fk": { + "name": "workflow_schedule_workflow_id_workflow_id_fk", + "tableFrom": "workflow_schedule", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_schedule_deployment_version_id_workflow_deployment_version_id_fk": { + "name": "workflow_schedule_deployment_version_id_workflow_deployment_version_id_fk", + "tableFrom": "workflow_schedule", + "columnsFrom": ["deployment_version_id"], + "tableTo": "workflow_deployment_version", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_schedule_deployment_operation_id_workflow_deployment_operation_id_fk": { + "name": "workflow_schedule_deployment_operation_id_workflow_deployment_operation_id_fk", + "tableFrom": "workflow_schedule", + "columnsFrom": ["deployment_operation_id"], + "tableTo": "workflow_deployment_operation", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "workflow_schedule_source_user_id_user_id_fk": { + "name": "workflow_schedule_source_user_id_user_id_fk", + "tableFrom": "workflow_schedule", + "columnsFrom": ["source_user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workflow_schedule_source_workspace_id_workspace_id_fk": { + "name": "workflow_schedule_source_workspace_id_workspace_id_fk", + "tableFrom": "workflow_schedule", + "columnsFrom": ["source_workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workflow_subflows_workflow_id_workflow_id_fk": { + "name": "workflow_subflows_workflow_id_workflow_id_fk", + "tableFrom": "workflow_subflows", + "columnsFrom": ["workflow_id"], + "tableTo": "workflow", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_organization_id_idx": { + "name": "workspace_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_mode_idx": { + "name": "workspace_mode_idx", + "columns": [ + { + "expression": "workspace_mode", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_inbox_provider_id_idx": { + "name": "workspace_inbox_provider_id_idx", + "columns": [ + { + "expression": "inbox_provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"workspace\".\"inbox_provider_id\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_owner_id_user_id_fk": { + "name": "workspace_owner_id_user_id_fk", + "tableFrom": "workspace", + "columnsFrom": ["owner_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_organization_id_organization_id_fk": { + "name": "workspace_organization_id_organization_id_fk", + "tableFrom": "workspace", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "workspace_billed_account_user_id_user_id_fk": { + "name": "workspace_billed_account_user_id_user_id_fk", + "tableFrom": "workspace", + "columnsFrom": ["billed_account_user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "no action" + }, + "workspace_forked_from_workspace_id_workspace_id_fk": { + "name": "workspace_forked_from_workspace_id_workspace_id_fk", + "tableFrom": "workspace", + "columnsFrom": ["forked_from_workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_byok_keys_workspace_id_workspace_id_fk": { + "name": "workspace_byok_keys_workspace_id_workspace_id_fk", + "tableFrom": "workspace_byok_keys", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_byok_keys_created_by_user_id_fk": { + "name": "workspace_byok_keys_created_by_user_id_fk", + "tableFrom": "workspace_byok_keys", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_environment_workspace_id_workspace_id_fk": { + "name": "workspace_environment_workspace_id_workspace_id_fk", + "tableFrom": "workspace_environment", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_file_deleted_at_idx": { + "name": "workspace_file_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_file\".\"deleted_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_file_workspace_id_workspace_id_fk": { + "name": "workspace_file_workspace_id_workspace_id_fk", + "tableFrom": "workspace_file", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_file_uploaded_by_user_id_fk": { + "name": "workspace_file_uploaded_by_user_id_fk", + "tableFrom": "workspace_file", + "columnsFrom": ["uploaded_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "workspace_file_key_unique": { + "name": "workspace_file_key_unique", + "columns": ["key"], + "nullsNotDistinct": false + } + }, + "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", + "columnsFrom": ["file_id"], + "tableTo": "workspace_files", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_file_search_build\".\"expires_at\" IS NOT NULL", + "concurrently": false + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": { + "fastupdate": "off" + }, + "method": "gin", + "concurrently": true + } + }, + "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", + "columnsFrom": ["build_id"], + "tableTo": "workspace_file_search_build", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_file_search_queue_workspace_fk": { + "name": "workspace_file_search_queue_workspace_fk", + "tableFrom": "workspace_file_search_dispatch_queue", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_file_search_index\".\"status\" = 'pending' AND \"workspace_file_search_index\".\"dispatched_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_file_search_index\".\"status\" = 'pending' AND \"workspace_file_search_index\".\"dispatched_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_file_search_index_file_fk": { + "name": "workspace_file_search_index_file_fk", + "tableFrom": "workspace_file_search_index", + "columnsFrom": ["file_id"], + "tableTo": "workspace_files", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_file_search_index_workspace_fk": { + "name": "workspace_file_search_index_workspace_fk", + "tableFrom": "workspace_file_search_index", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_file_search_revision\".\"status\" = 'pending' AND \"workspace_file_search_revision\".\"dispatched_at\" IS NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_file_search_revision\".\"status\" = 'pending' AND \"workspace_file_search_revision\".\"dispatched_at\" IS NOT NULL", + "concurrently": false + } + }, + "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", + "columnsFrom": ["file_id"], + "tableTo": "workspace_files", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "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", + "columnsFrom": ["build_id"], + "tableTo": "workspace_file_search_build", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "gin", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_file_search_segment_file_fk": { + "name": "workspace_file_search_segment_file_fk", + "tableFrom": "workspace_file_search_segment", + "columnsFrom": ["file_id"], + "tableTo": "workspace_files", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_file_search_segment_workspace_fk": { + "name": "workspace_file_search_segment_workspace_fk", + "tableFrom": "workspace_file_search_segment", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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", + "columnsFrom": ["file_id"], + "tableTo": "workspace_files", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_file_version_key_unique": { + "name": "workspace_file_version_key_unique", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_file_version\".\"superseded_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_file_version_file_id_workspace_files_id_fk": { + "name": "workspace_file_version_file_id_workspace_files_id_fk", + "tableFrom": "workspace_file_version", + "columnsFrom": ["file_id"], + "tableTo": "workspace_files", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_file_version_workspace_id_workspace_id_fk": { + "name": "workspace_file_version_workspace_id_workspace_id_fk", + "tableFrom": "workspace_file_version", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_files\".\"deleted_at\" IS NULL", + "concurrently": false + }, + "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\", '')", + "isExpression": true, + "asc": true, + "nulls": "last" + }, + { + "expression": "original_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"workspace_files\".\"deleted_at\" IS NULL AND \"workspace_files\".\"context\" = 'workspace' AND \"workspace_files\".\"workspace_id\" IS NOT NULL", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_files\".\"deleted_at\" IS NULL AND \"workspace_files\".\"context\" = 'workspace' AND \"workspace_files\".\"workspace_id\" IS NOT NULL", + "concurrently": true + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_files\".\"context\" = 'mothership' AND \"workspace_files\".\"chat_id\" IS NOT NULL", + "concurrently": false + }, + "workspace_files_organization_id_idx": { + "name": "workspace_files_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_files_key_idx": { + "name": "workspace_files_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_files_user_id_idx": { + "name": "workspace_files_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_files_workspace_id_idx": { + "name": "workspace_files_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_files_folder_id_idx": { + "name": "workspace_files_folder_id_idx", + "columns": [ + { + "expression": "folder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_files_context_idx": { + "name": "workspace_files_context_idx", + "columns": [ + { + "expression": "context", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_files_chat_id_idx": { + "name": "workspace_files_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_files_deleted_at_idx": { + "name": "workspace_files_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "where": "\"workspace_files\".\"deleted_at\" IS NOT NULL", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_files_user_id_user_id_fk": { + "name": "workspace_files_user_id_user_id_fk", + "tableFrom": "workspace_files", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_files_workspace_id_workspace_id_fk": { + "name": "workspace_files_workspace_id_workspace_id_fk", + "tableFrom": "workspace_files", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_files_organization_id_organization_id_fk": { + "name": "workspace_files_organization_id_organization_id_fk", + "tableFrom": "workspace_files", + "columnsFrom": ["organization_id"], + "tableTo": "organization", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_files_folder_id_folder_id_fk": { + "name": "workspace_files_folder_id_folder_id_fk", + "tableFrom": "workspace_files", + "columnsFrom": ["folder_id"], + "tableTo": "folder", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + }, + "workspace_files_chat_id_copilot_chats_id_fk": { + "name": "workspace_files_chat_id_copilot_chats_id_fk", + "tableFrom": "workspace_files", + "columnsFrom": ["chat_id"], + "tableTo": "copilot_chats", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["child_workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["child_workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["child_workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_fork_promote_run_created_by_user_id_fk": { + "name": "workspace_fork_promote_run_created_by_user_id_fk", + "tableFrom": "workspace_fork_promote_run", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "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", + "columnsFrom": ["child_workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_fork_resource_map_created_by_user_id_fk": { + "name": "workspace_fork_resource_map_created_by_user_id_fk", + "tableFrom": "workspace_fork_resource_map", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_operation_receipt_workspace_id_workspace_id_fk": { + "name": "workspace_operation_receipt_workspace_id_workspace_id_fk", + "tableFrom": "workspace_operation_receipt", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_sandbox_workspace_idx": { + "name": "workspace_sandbox_workspace_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "workspace_sandbox_spec_hash_idx": { + "name": "workspace_sandbox_spec_hash_idx", + "columns": [ + { + "expression": "spec_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_sandbox_workspace_id_workspace_id_fk": { + "name": "workspace_sandbox_workspace_id_workspace_id_fk", + "tableFrom": "workspace_sandbox", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_sandbox_created_by_user_id_fk": { + "name": "workspace_sandbox_created_by_user_id_fk", + "tableFrom": "workspace_sandbox", + "columnsFrom": ["created_by"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "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, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "workspace_visit_user_id_user_id_fk": { + "name": "workspace_visit_user_id_user_id_fk", + "tableFrom": "workspace_visit", + "columnsFrom": ["user_id"], + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "workspace_visit_workspace_id_workspace_id_fk": { + "name": "workspace_visit_workspace_id_workspace_id_fk", + "tableFrom": "workspace_visit", + "columnsFrom": ["workspace_id"], + "tableTo": "workspace", + "columnsTo": ["id"], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "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" + ] + }, + "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": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json index d97e885e7ee..de97c5ada09 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -2752,6 +2752,13 @@ "when": 1790912634834, "tag": "0393_project_foundation", "breakpoints": true + }, + { + "idx": 394, + "version": "7", + "when": 1790973974548, + "tag": "0394_project_membership_enforcement", + "breakpoints": true } ] } diff --git a/packages/db/schema.ts b/packages/db/schema.ts index ad9cf477872..5e76f2a765a 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2089,7 +2089,7 @@ export const project = pgTable( }) ) -// contract-pending(after project writers are fully deployed and backfill validates): enforce exactly-one membership and active Project environment minimums at commit. +/** Deferred membership and lifecycle triggers are installed by 0394 after the Project backfill. */ export const projectWorkspace = pgTable( 'project_workspace', { diff --git a/packages/db/scripts/project-contract-required.ts b/packages/db/scripts/project-contract-required.ts new file mode 100644 index 00000000000..92cb6aab927 --- /dev/null +++ b/packages/db/scripts/project-contract-required.ts @@ -0,0 +1,36 @@ +import journal from '@sim/db/migrations/meta/_journal.json' +import { createLogger } from '@sim/logger' +import postgres from 'postgres' + +const logger = createLogger('ProjectContractPreflight') +const migration = journal.entries.find( + (entry) => entry.tag === '0394_project_membership_enforcement' +) +const url = process.env.MIGRATION_DATABASE_URL || process.env.DATABASE_URL +if (!migration || !url) + throw new Error('Project contract preflight requires migration metadata and a database URL') +const sql = postgres(url, { + max: 1, + connect_timeout: 10, + connection: { statement_timeout: 10_000 }, +}) +try { + const [tables] = await sql<{ workspace: string | null; journal: string | null }[]>` + SELECT to_regclass('public.workspace')::text AS workspace, + to_regclass('drizzle.__drizzle_migrations')::text AS journal + ` + if (!tables) throw new Error('Could not inspect migration prerequisites') + let required = Boolean(tables.workspace) + if (required && tables.journal) { + const [applied] = await sql<{ complete: boolean }[]>` + SELECT EXISTS (SELECT 1 FROM drizzle.__drizzle_migrations WHERE created_at >= ${migration.when}) AS complete + ` + required = !applied?.complete + } + process.stdout.write(`required=${required}\n`) +} catch { + logger.error('Cannot establish Project contract prerequisites; refusing migration') + process.exitCode = 1 +} finally { + await sql.end({ timeout: 5 }) +} diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts new file mode 100644 index 00000000000..75951364478 --- /dev/null +++ b/packages/db/scripts/project-contract.integration.ts @@ -0,0 +1,270 @@ +import { execFile } from 'node:child_process' +import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { promisify } from 'node:util' +import journal from '@sim/db/migrations/meta/_journal.json' +import { backfillProjects } from '@sim/db/project-backfill' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { createDeferred } from '@sim/testing/helpers/deferred' +import { getPostgresErrorCode } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { drizzle } from 'drizzle-orm/postgres-js' +import { migrate } from 'drizzle-orm/postgres-js/migrator' +import postgres, { type Sql } from 'postgres' +import { describe, expect, it } from 'vitest' + +const migration = await readFile( + new URL('../migrations/0394_project_membership_enforcement.sql', import.meta.url), + 'utf8' +) + +async function database(run: (sql: Sql, url: string) => Promise) { + const admin = postgres(readTestDatabaseUrl(), { max: 1 }) + const name = `project_contract_test_${generateId().replaceAll('-', '')}` + const url = new URL(readTestDatabaseUrl()) + url.pathname = `/${name}` + await admin.unsafe(`CREATE DATABASE "${name}"`) + const sql = postgres(url.toString(), { max: 4, onnotice: () => undefined }) + try { + await sql.unsafe(` + CREATE TABLE "user" (id text PRIMARY KEY); + CREATE TABLE organization (id text PRIMARY KEY); + CREATE TABLE workspace (id text PRIMARY KEY, name text NOT NULL, owner_id text NOT NULL, + organization_id text, archived_at timestamp, forked_from_workspace_id text REFERENCES workspace(id) ON DELETE SET NULL); + CREATE INDEX workspace_parent_idx ON workspace(forked_from_workspace_id); + CREATE TABLE workflow (id text PRIMARY KEY, workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, archived_at timestamp); + INSERT INTO "user" VALUES ('owner'); INSERT INTO organization VALUES ('org'); + `) + await sql.unsafe( + await readFile(new URL('../migrations/0393_project_foundation.sql', import.meta.url), 'utf8') + ) + await run(sql, url.toString()) + } finally { + await sql.end({ timeout: 2 }) + await admin.unsafe(`DROP DATABASE "${name}" WITH (FORCE)`) + await admin.end() + } +} + +async function enforce(sql: Sql) { + const reserved = await sql.reserve() + try { + await reserved.unsafe(migration) + } catch (error) { + await reserved.unsafe('ROLLBACK') + throw error + } finally { + reserved.release() + } +} + +async function backfill(sql: Sql) { + const plan = await backfillProjects(sql, { mode: 'dry-run', databaseId: 'contract-test' }) + const applied = await backfillProjects(sql, { mode: 'apply', databaseId: 'contract-test', plan }) + expect(applied.conflicts).toEqual([]) + expect((await backfillProjects(sql, { mode: 'verify', databaseId: 'contract-test' })).ready).toBe( + true + ) +} + +async function seed(sql: Sql) { + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Production', 'owner')` + await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) VALUES ('child', 'Staging', 'owner', 'root')` + await sql`INSERT INTO workflow VALUES ('flow', 'root', NULL)` + await backfill(sql) +} + +const constraintFailure = (error: unknown) => getPostgresErrorCode(error) === '23514' + +describe('Project expand/backfill/contract against PostgreSQL', () => { + it('requires rollout evidence only for an existing database with enforcement pending', async () => { + await database(async (sql, url) => { + const run = () => + promisify(execFile)('bun', ['--no-env-file', 'scripts/project-contract-required.ts'], { + cwd: new URL('..', import.meta.url), + env: { ...process.env, DATABASE_URL: url, MIGRATION_DATABASE_URL: url }, + }) + expect((await run()).stdout.trim()).toBe('required=true') + await sql`CREATE SCHEMA drizzle` + await sql`CREATE TABLE drizzle.__drizzle_migrations (created_at bigint)` + const entry = journal.entries.find( + (item) => item.tag === '0394_project_membership_enforcement' + ) + if (!entry) throw new Error('Missing contract migration') + await sql`INSERT INTO drizzle.__drizzle_migrations VALUES (${entry.when - 1})` + expect((await run()).stdout.trim()).toBe('required=true') + await sql`INSERT INTO drizzle.__drizzle_migrations VALUES (${entry.when})` + expect((await run()).stdout.trim()).toBe('required=false') + await sql`DROP TABLE workspace CASCADE` + await sql`DROP SCHEMA drizzle CASCADE` + expect((await run()).stdout.trim()).toBe('required=false') + }) + }) + + it('journals enforcement through the Drizzle runner and safely resumes after a failed precheck', async () => { + const directory = await mkdtemp(join(tmpdir(), 'project-contract-runner-')) + try { + await mkdir(join(directory, 'meta')) + await writeFile(join(directory, '0394_project_membership_enforcement.sql'), migration) + await writeFile( + join(directory, 'meta/_journal.json'), + JSON.stringify({ + ...journal, + entries: journal.entries.filter( + (entry) => entry.tag === '0394_project_membership_enforcement' + ), + }) + ) + await database(async (sql) => { + const run = () => migrate(drizzle(sql), { migrationsFolder: directory }) + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('legacy', 'Legacy', 'owner')` + await expect(run()).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55000' + ) + expect(await sql`SELECT * FROM drizzle.__drizzle_migrations`).toHaveLength(0) + await backfill(sql) + await run() + await run() + expect(await sql`SELECT * FROM drizzle.__drizzle_migrations`).toHaveLength(1) + await expect(sql`DELETE FROM project_workspace`).rejects.toSatisfy(constraintFailure) + }) + } finally { + await rm(directory, { recursive: true, force: true }) + } + }) + + it('refuses missing backfill and installs no partial enforcement, then accepts a verified backfill', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('legacy', 'Legacy', 'owner')` + await expect(enforce(sql)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55000' + ) + expect( + await sql`SELECT 1 FROM pg_trigger WHERE tgname = 'project_contract_check'` + ).toHaveLength(0) + await backfill(sql) + await enforce(sql) + await expect( + sql`INSERT INTO workspace (id, name, owner_id) VALUES ('unassigned', 'Invalid', 'owner')` + ).rejects.toSatisfy(constraintFailure) + expect(await sql`SELECT 1 FROM workspace WHERE id = 'unassigned'`).toHaveLength(0) + }) + }) + + it('supports a fresh database, repeated installation, and atomic first-environment creation', async () => { + await database(async (sql) => { + await enforce(sql) + await enforce(sql) + await sql.begin(async (tx) => { + await tx`INSERT INTO project (id, name, owner_id) VALUES ('new', 'New', 'owner')` + await tx`INSERT INTO workspace (id, name, owner_id) VALUES ('first', 'First', 'owner')` + await tx`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('new', 'first')` + await tx`INSERT INTO workflow VALUES ('flow', 'first', NULL)` + }) + await expect( + sql`INSERT INTO project (id, name, owner_id) VALUES ('empty', 'Empty', 'owner')` + ).rejects.toSatisfy(constraintFailure) + expect(await sql`SELECT 1 FROM project`).toHaveLength(1) + }) + }) + + it('rejects invalid archive/scope/fork changes and commits an atomic subtree disconnect', async () => { + await database(async (sql) => { + await seed(sql) + await enforce(sql) + await expect(sql`UPDATE project SET archived_at = now()`).rejects.toSatisfy(constraintFailure) + await expect( + sql`UPDATE workspace SET organization_id = 'org' WHERE id = 'child'` + ).rejects.toSatisfy(constraintFailure) + await expect( + sql`DELETE FROM project_workspace WHERE workspace_id = 'child'` + ).rejects.toSatisfy(constraintFailure) + await expect( + sql.begin(async (tx) => { + await tx`INSERT INTO project (id, name, owner_id) VALUES ('detached', 'Detached', 'owner')` + await tx`UPDATE project_workspace SET project_id = 'detached' WHERE workspace_id = 'child'` + }) + ).rejects.toSatisfy(constraintFailure) + await sql.begin(async (tx) => { + await tx`INSERT INTO project (id, name, owner_id) VALUES ('detached', 'Detached', 'owner')` + await tx`UPDATE project_workspace SET project_id = 'detached' WHERE workspace_id = 'child'` + await tx`UPDATE workspace SET forked_from_workspace_id = NULL WHERE id = 'child'` + }) + expect(await sql`SELECT DISTINCT project_id FROM project_workspace`).toHaveLength(2) + }) + }) + + it('archives all Project environments and workflows together and refuses later active workflow insertion', async () => { + await database(async (sql) => { + await seed(sql) + await enforce(sql) + await expect( + sql.begin(async (tx) => { + await tx`UPDATE project SET archived_at = now()` + await tx`UPDATE workspace SET archived_at = now()` + }) + ).rejects.toSatisfy(constraintFailure) + await sql.begin(async (tx) => { + await tx`UPDATE workflow SET archived_at = now()` + await tx`UPDATE workspace SET archived_at = now()` + await tx`UPDATE project SET archived_at = now()` + }) + await expect(sql`INSERT INTO workflow VALUES ('late', 'child', NULL)`).rejects.toSatisfy( + constraintFailure + ) + await expect( + sql`UPDATE workspace SET archived_at = NULL WHERE id = 'root'` + ).rejects.toSatisfy(constraintFailure) + expect(await sql`SELECT 1 FROM workspace WHERE archived_at IS NULL`).toHaveLength(0) + }) + }) + + it.each(['read committed', 'repeatable read'] as const)( + 'prevents concurrent last-environment removal under %s', + async (isolation) => { + await database(async (sql) => { + await seed(sql) + await enforce(sql) + const archived = createDeferred() + const release = createDeferred() + const first = sql.begin(async (tx) => { + await tx`UPDATE workspace SET archived_at = now() WHERE id = 'root'` + archived.resolve() + await release.promise + }) + await archived.promise + const started = createDeferred() + const second = sql + .begin(`isolation level ${isolation}`, async (tx) => { + await tx`SELECT count(*) FROM workspace WHERE archived_at IS NULL` + started.resolve() + await tx`UPDATE workspace SET archived_at = now() WHERE id = 'child'` + }) + .then( + () => null, + (error: unknown) => error + ) + await started.promise + release.resolve() + await first + const failure = await second + expect(['23514', '40001', '40P01']).toContain(getPostgresErrorCode(failure)) + expect(await sql`SELECT 1 FROM workspace WHERE archived_at IS NULL`).toHaveLength(1) + }) + } + ) + + it('refuses enforcement when an archived Project retains an active workflow', async () => { + await database(async (sql) => { + await seed(sql) + await sql`UPDATE workspace SET archived_at = now()` + await sql`UPDATE project SET archived_at = now()` + await expect(enforce(sql)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55000' + ) + await sql`UPDATE workflow SET archived_at = now()` + await enforce(sql) + }) + }) +}) From 523338a34d84bb908ffcd3aff302fdfbc85c6c4f Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 15:31:39 -0700 Subject: [PATCH 007/113] fix(projects): retry writes after concurrent membership changes --- .../0394_project_membership_enforcement.sql | 9 +++- .../scripts/project-contract.integration.ts | 47 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/packages/db/migrations/0394_project_membership_enforcement.sql b/packages/db/migrations/0394_project_membership_enforcement.sql index 92cbd512c4d..b5c669807cd 100644 --- a/packages/db/migrations/0394_project_membership_enforcement.sql +++ b/packages/db/migrations/0394_project_membership_enforcement.sql @@ -142,8 +142,15 @@ BEGIN IF TG_OP <> 'INSERT' THEN previous_id := OLD.workspace_id; END IF; IF TG_OP <> 'DELETE' THEN next_id := NEW.workspace_id; END IF; END IF; - SELECT array_agg(project_id) INTO owners FROM project_workspace WHERE workspace_id IN (previous_id, next_id); + SELECT array_agg(project_id ORDER BY workspace_id) INTO owners FROM project_workspace WHERE workspace_id IN (previous_id, next_id); PERFORM project_contract_lock_projects(owners); + IF owners IS DISTINCT FROM ( + SELECT array_agg(project_id ORDER BY workspace_id) FROM project_workspace + WHERE workspace_id IN (previous_id, next_id) + ) THEN + RAISE EXCEPTION 'Environment changed Projects while acquiring its lifecycle lock; retry the transaction' + USING ERRCODE = '40001'; + END IF; END IF; IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; RETURN NEW; diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 75951364478..970966c5521 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -8,6 +8,7 @@ import { backfillProjects } from '@sim/db/project-backfill' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { createDeferred } from '@sim/testing/helpers/deferred' import { getPostgresErrorCode } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { drizzle } from 'drizzle-orm/postgres-js' import { migrate } from 'drizzle-orm/postgres-js/migrator' @@ -220,6 +221,52 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) + it('retries a workflow write when its environment moves Projects while the writer waits', async () => { + await database(async (sql) => { + await seed(sql) + await enforce(sql) + const moved = createDeferred() + const release = createDeferred() + const transfer = sql.begin(async (tx) => { + await tx`INSERT INTO project (id, name, owner_id) VALUES ('destination', 'Destination', 'owner')` + await tx`UPDATE workspace SET forked_from_workspace_id = NULL WHERE id = 'child'` + await tx`UPDATE project_workspace SET project_id = 'destination' WHERE workspace_id = 'child'` + moved.resolve() + await release.promise + }) + await moved.promise + const writer = sql + .begin(async (tx) => { + await tx`SET LOCAL application_name = 'project_contract_stale_writer'` + await tx`INSERT INTO workflow VALUES ('racing', 'child', NULL)` + }) + .then( + () => null, + (error: unknown) => error + ) + try { + let waiting = false + for (let attempt = 0; attempt < 100; attempt++) { + const rows = + await sql`SELECT 1 FROM pg_stat_activity WHERE application_name = 'project_contract_stale_writer' AND wait_event_type = 'Lock'` + if (rows.length) { + waiting = true + break + } + await sleep(10) + } + expect(waiting).toBe(true) + } finally { + release.resolve() + } + await transfer + expect(getPostgresErrorCode(await writer)).toBe('40001') + expect(await sql`SELECT 1 FROM workflow WHERE id = 'racing'`).toHaveLength(0) + await sql`INSERT INTO workflow VALUES ('retried', 'child', NULL)` + expect(await sql`SELECT 1 FROM workflow WHERE id = 'retried'`).toHaveLength(1) + }) + }) + it.each(['read committed', 'repeatable read'] as const)( 'prevents concurrent last-environment removal under %s', async (isolation) => { From dc5e3013af1281590cece9967af4786f4231e42b Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 15:58:01 -0700 Subject: [PATCH 008/113] fix(projects): align integration fixtures with membership constraints --- .../member-sync/route.integration.ts | 3 +- .../app/api/v1/knowledge/route.integration.ts | 3 +- .../cleanup-table-row-ttl.integration.ts | 11 ++- ...rganization-personal-tokens.integration.ts | 8 ++- .../execution-environment.integration.ts | 5 +- .../application-acl.integration.ts | 3 +- .../__integration__/coda-live.integration.ts | 3 +- .../confluence-enrollment.integration.ts | 3 +- .../confluence-identity.integration.ts | 3 +- .../connector-deferral.integration.ts | 3 +- .../connector-lease-pages.integration.ts | 3 +- .../connector-lifecycle-locks.integration.ts | 3 +- .../connector-partition-work.integration.ts | 3 +- .../connector-permissions.integration.ts | 3 +- ...tor-persistence-regressions.integration.ts | 3 +- .../connector-save-concurrency.integration.ts | 3 +- .../connector-upload.integration.ts | 3 +- .../directory-sync.integration.ts | 3 +- ...dormant-processing-recovery.integration.ts | 3 +- .../dormant-search-processing.integration.ts | 3 +- .../embedding-insert-batches.integration.ts | 3 +- ...bedding-processing-recovery.integration.ts | 3 +- .../excluded-member-documents.integration.ts | 3 +- ...xecution-archive-provenance.integration.ts | 3 +- .../external-file-provenance.integration.ts | 3 +- .../filtered-search.integration.ts | 3 +- .../github-member.integration.ts | 3 +- .../gitlab-federated.integration.ts | 3 +- .../gitlab-live.integration.ts | 3 +- .../gitlab-workspace.integration.ts | 3 +- .../gmail-member.integration.ts | 3 +- .../google-calendar-member.integration.ts | 3 +- .../google-drive-live.integration.ts | 3 +- .../google-drive-shortcuts.integration.ts | 3 +- .../jira-member.integration.ts | 3 +- .../kb-block-search.integration.ts | 3 +- .../knowledge-base-deletion.integration.ts | 3 +- .../knowledge-projection.integration.ts | 3 +- .../listing-continuation.integration.ts | 3 +- .../member-document-lifecycle.integration.ts | 3 +- .../member-scope-renewal.integration.ts | 3 +- .../ocr-input-failures.integration.ts | 3 +- .../processing-lock-scope.integration.ts | 3 +- ...rovider-processing-recovery.integration.ts | 3 +- .../providers-live.integration.ts | 3 +- .../purged-detach-reservation.integration.ts | 3 +- .../__integration__/scale.integration.ts | 3 +- .../search-index-policy.integration.ts | 3 +- .../search-latency.integration.ts | 3 +- .../search-reference-batching.integration.ts | 3 +- .../search-source-pagination.integration.ts | 3 +- .../search-source-progress.integration.ts | 9 +-- .../search-source-setup.integration.ts | 7 +- .../seed-source-access-fixture.ts | 4 +- .../slack-empty-threads.integration.ts | 3 +- .../storage-accounting.integration.ts | 3 +- .../storage-cleanup.integration.ts | 3 +- .../stored-document-recovery.integration.ts | 3 +- .../upload-read-provenance.integration.ts | 3 +- .../user-document-visibility.integration.ts | 3 +- .../workspace-import.integration.ts | 3 +- ...orkspace-kb-document-access.integration.ts | 5 +- .../workspace-lifecycle.integration.ts | 21 +++++- .../completion-ledger-order.integration.ts | 5 +- .../execution/start-execution.integration.ts | 5 +- .../workbench-file-provenance.integration.ts | 5 +- .../async-runs/orphaned-runs.integration.ts | 5 +- .../session/recovery-storm.integration.ts | 5 +- .../session/replay-budget.integration.ts | 5 +- .../request/session/replay-gap.integration.ts | 5 +- .../session/stream-recovery.integration.ts | 5 +- .../workflow-client-settlement.integration.ts | 5 +- .../application/prepare-wake.integration.ts | 5 +- .../__integration__/foundation.integration.ts | 26 ++++++- .../copilot-nested-selectors.integration.ts | 5 +- .../lib/table/rows/row-writes.integration.ts | 11 ++- .../__integration__/file-names.integration.ts | 3 +- .../file-versions.integration.ts | 3 +- .../trigger-block-deployment.integration.ts | 12 ++-- .../compare-workflow-versions.integration.ts | 6 +- .../read-workflow-version.integration.ts | 5 +- .../workspace-rows.integration.ts | 5 +- .../executor/pause-persistence.integration.ts | 5 +- .../__integration__/fork-sync.integration.ts | 36 +++------- .../__integration__/http-cli.integration.ts | 11 +-- .../mapped-import.integration.ts | 5 +- .../__integration__/pagination.integration.ts | 14 ++-- .../__integration__/receipts.integration.ts | 5 +- apps/sim/scripts/test-scim-e2e.ts | 5 ++ .../test-workflow-version-compare-e2e.ts | 7 +- .../search-retirement.integration.ts | 8 ++- packages/db/testing/workspace-fixtures.ts | 68 +++++++++++++++++++ scripts/test-integration.ts | 12 ++-- 93 files changed, 374 insertions(+), 163 deletions(-) create mode 100644 packages/db/testing/workspace-fixtures.ts diff --git a/apps/sim/app/api/knowledge/connectors/member-sync/route.integration.ts b/apps/sim/app/api/knowledge/connectors/member-sync/route.integration.ts index 66e77f9ebec..3a4b0660998 100644 --- a/apps/sim/app/api/knowledge/connectors/member-sync/route.integration.ts +++ b/apps/sim/app/api/knowledge/connectors/member-sync/route.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * The member sync scheduler's reclaim against real PostgreSQL: a members-mode connector whose * member lease went stale is put back on the failure ladder, and a connector in any other access @@ -51,7 +52,7 @@ describe('member sync reclaim in PostgreSQL', () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await db.$client.end() diff --git a/apps/sim/app/api/v1/knowledge/route.integration.ts b/apps/sim/app/api/v1/knowledge/route.integration.ts index ab75f05a29d..f19e6e55c55 100644 --- a/apps/sim/app/api/v1/knowledge/route.integration.ts +++ b/apps/sim/app/api/v1/knowledge/route.integration.ts @@ -7,6 +7,7 @@ import type { Principal } from '@sim/auth/principal' import { db } from '@sim/db' import { document, organization, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { authMock, authMockFns, createMockRequest } from '@sim/testing' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' @@ -85,7 +86,7 @@ describe('knowledge-base document totals in PostgreSQL', () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) vi.unstubAllGlobals() diff --git a/apps/sim/background/cleanup-table-row-ttl.integration.ts b/apps/sim/background/cleanup-table-row-ttl.integration.ts index 6e5ca47e380..e8ceffca720 100644 --- a/apps/sim/background/cleanup-table-row-ttl.integration.ts +++ b/apps/sim/background/cleanup-table-row-ttl.integration.ts @@ -110,8 +110,12 @@ describe.skipIf(!migrated)('Expiration with real PostgreSQL transactions', () => beforeAll(async () => { await control`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at) VALUES (${userId}, 'Expiration integration fixture', ${`${userId}@example.test`}, true, now(), now())` - await control`INSERT INTO workspace (id, name, owner_id, billed_account_user_id) + await control.begin(async (tx) => { + await tx`INSERT INTO workspace (id, name, owner_id, billed_account_user_id) VALUES (${workspaceId}, 'Expiration integration fixtures', ${userId}, ${userId})` + await tx`INSERT INTO project (id, name, owner_id) VALUES (${workspaceId}, 'Fixture project', ${userId})` + await tx`INSERT INTO project_workspace (project_id, workspace_id) VALUES (${workspaceId}, ${workspaceId})` + }) }) beforeEach(async () => { @@ -126,7 +130,10 @@ describe.skipIf(!migrated)('Expiration with real PostgreSQL transactions', () => }) afterAll(async () => { - await control`DELETE FROM workspace WHERE id = ${workspaceId}` + await control.begin(async (tx) => { + await tx`DELETE FROM workspace WHERE id = ${workspaceId}` + await tx`DELETE FROM project WHERE id = ${workspaceId}` + }) await control`DELETE FROM "user" WHERE id = ${userId}` writeFileSync( join(tmpdir(), 'expiration-qa-measurements.json'), diff --git a/apps/sim/lib/credentials/__integration__/organization-personal-tokens.integration.ts b/apps/sim/lib/credentials/__integration__/organization-personal-tokens.integration.ts index fb1f2ffdd7c..1287fbbd393 100644 --- a/apps/sim/lib/credentials/__integration__/organization-personal-tokens.integration.ts +++ b/apps/sim/lib/credentials/__integration__/organization-personal-tokens.integration.ts @@ -11,6 +11,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { sha256Hex } from '@sim/security/hash' import { generateId } from '@sim/utils/id' import { and, eq, inArray } from 'drizzle-orm' @@ -106,7 +107,8 @@ describe('organization personal tokens', () => { { id: generateId(), organizationId: ids.org, userId: ids.owner, role: 'member' }, { id: generateId(), organizationId: ids.org, userId: ids.other, role: 'admin' }, ]) - await db.insert(workspace).values( + await insertWorkspaceFixture( + db, [ids.first, ids.second, ids.foreign].map((id) => ({ id, name: 'Token fixture workspace', @@ -187,7 +189,7 @@ describe('organization personal tokens', () => { await db .delete(credentialGroup) .where(inArray(credentialGroup.id, [ids.group, ids.legacyGroup])) - await db.delete(workspace).where(inArray(workspace.id, [ids.first, ids.second, ids.foreign])) + await deleteWorkspaceFixture(db, inArray(workspace.id, [ids.first, ids.second, ids.foreign])) await db.delete(organization).where(inArray(organization.id, [ids.org, ids.foreignOrg])) await db.delete(user).where(inArray(user.id, [ids.owner, ids.other])) }) @@ -287,7 +289,7 @@ describe('organization personal tokens', () => { expect.objectContaining({ id: ids.token, workspaceId: null, organizationId: ids.org }), ]) } - await db.delete(workspace).where(eq(workspace.id, ids.first)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.first)) await expect(resolve()).resolves.toMatchObject({ accessToken: tokenSecret }) }) diff --git a/apps/sim/lib/environment/execution-environment.integration.ts b/apps/sim/lib/environment/execution-environment.integration.ts index b38e792f4a6..8f5e8b45940 100644 --- a/apps/sim/lib/environment/execution-environment.integration.ts +++ b/apps/sim/lib/environment/execution-environment.integration.ts @@ -5,6 +5,7 @@ */ import { db } from '@sim/db' import { environment, permissions, user, workspace, workspaceEnvironment } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -41,7 +42,7 @@ beforeAll(async () => { ...(id === suspended ? { banned: true } : {}), })) ) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Environment', ownerId: owner, @@ -73,7 +74,7 @@ beforeAll(async () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(inArray(user.id, userIds)) }) diff --git a/apps/sim/lib/knowledge/__integration__/application-acl.integration.ts b/apps/sim/lib/knowledge/__integration__/application-acl.integration.ts index 814e7f5270e..358e2fed5ac 100644 --- a/apps/sim/lib/knowledge/__integration__/application-acl.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/application-acl.integration.ts @@ -25,6 +25,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -278,7 +279,7 @@ describe('indexed source content through real application access', () => { CONFLUENCE_CLIENT_ID: previousConfluenceClient.id, CONFLUENCE_CLIENT_SECRET: previousConfluenceClient.secret, }) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, aliceId)) await db.delete(user).where(eq(user.id, bobId)) await rm(fixtures.storageRoot, { recursive: true, force: true }) diff --git a/apps/sim/lib/knowledge/__integration__/coda-live.integration.ts b/apps/sim/lib/knowledge/__integration__/coda-live.integration.ts index e40cdb28090..3b12fe7cb0b 100644 --- a/apps/sim/lib/knowledge/__integration__/coda-live.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/coda-live.integration.ts @@ -17,6 +17,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { serializeSignedCookie } from 'better-call' @@ -225,7 +226,7 @@ describe.skipIf(!tokenPath || !fixturePath || !secondEmail)( .where(eq(document.knowledgeBaseId, ids.knowledgeBaseId)) for (const row of rows) if (row.storageKey) await deleteFile({ key: row.storageKey, context: 'knowledge-base' }) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(eq(user.id, ids.aliceId)) await db.delete(user).where(eq(user.id, ids.bobId)) diff --git a/apps/sim/lib/knowledge/__integration__/confluence-enrollment.integration.ts b/apps/sim/lib/knowledge/__integration__/confluence-enrollment.integration.ts index d9709792656..d60d0e07bb8 100644 --- a/apps/sim/lib/knowledge/__integration__/confluence-enrollment.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/confluence-enrollment.integration.ts @@ -15,6 +15,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' @@ -99,7 +100,7 @@ describe('Confluence mirrored-identity self-enrollment', () => { CONFLUENCE_CLIENT_SECRET: previousClient.secret, }) for (const fixture of [ids, foreign]) { - await db.delete(workspace).where(eq(workspace.id, fixture.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, fixture.workspaceId)) await db.delete(user).where(eq(user.id, fixture.aliceId)) await db.delete(user).where(eq(user.id, fixture.bobId)) } diff --git a/apps/sim/lib/knowledge/__integration__/confluence-identity.integration.ts b/apps/sim/lib/knowledge/__integration__/confluence-identity.integration.ts index 3cb48da93be..5393141cc9a 100644 --- a/apps/sim/lib/knowledge/__integration__/confluence-identity.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/confluence-identity.integration.ts @@ -9,6 +9,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -181,7 +182,7 @@ describe('Confluence identities with hidden directory email', () => { afterAll(async () => { vi.unstubAllGlobals() Object.assign(env, previousConfluenceClient) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where(eq(user.id, ids.aliceId)) await db.delete(user).where(eq(user.id, ids.bobId)) await db.$client.end() diff --git a/apps/sim/lib/knowledge/__integration__/connector-deferral.integration.ts b/apps/sim/lib/knowledge/__integration__/connector-deferral.integration.ts index 987a117173e..74bc6686252 100644 --- a/apps/sim/lib/knowledge/__integration__/connector-deferral.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/connector-deferral.integration.ts @@ -8,6 +8,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' @@ -88,7 +89,7 @@ describe('durable connector capacity deferrals', () => { }) afterAll(async () => { await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) vi.restoreAllMocks() vi.unstubAllGlobals() diff --git a/apps/sim/lib/knowledge/__integration__/connector-lease-pages.integration.ts b/apps/sim/lib/knowledge/__integration__/connector-lease-pages.integration.ts index 9da5961aa1f..ac93a5e6243 100644 --- a/apps/sim/lib/knowledge/__integration__/connector-lease-pages.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/connector-lease-pages.integration.ts @@ -19,6 +19,7 @@ import { } from '@sim/db/schema' import { installProjectionSourceAcl } from '@sim/db/script-migrations/0021_embedding_search_connector' import { installKnowledgeProjectionAsync } from '@sim/db/script-migrations/0024_knowledge_projection_async' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' import postgres from 'postgres' @@ -141,7 +142,7 @@ describe('connector lease ACL pages in PostgreSQL', () => { await db.execute(sql`DROP TRIGGER IF EXISTS fail_after_acl_writes ON document`) await db.execute(sql`DELETE FROM lease_page_acl_writes`) await db.execute(sql`DELETE FROM lease_page_projection_writes`) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) }) diff --git a/apps/sim/lib/knowledge/__integration__/connector-lifecycle-locks.integration.ts b/apps/sim/lib/knowledge/__integration__/connector-lifecycle-locks.integration.ts index 8d59264625a..6553fa846e8 100644 --- a/apps/sim/lib/knowledge/__integration__/connector-lifecycle-locks.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/connector-lifecycle-locks.integration.ts @@ -9,6 +9,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNotNull, sql } from 'drizzle-orm' import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -97,7 +98,7 @@ describe('source lifecycle KB guards', () => { }) afterEach(async () => { vi.restoreAllMocks() - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) }) diff --git a/apps/sim/lib/knowledge/__integration__/connector-partition-work.integration.ts b/apps/sim/lib/knowledge/__integration__/connector-partition-work.integration.ts index 1b6d75b2985..935f24ac898 100644 --- a/apps/sim/lib/knowledge/__integration__/connector-partition-work.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/connector-partition-work.integration.ts @@ -7,6 +7,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { and, eq, inArray, sql } from 'drizzle-orm' import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { z } from 'zod' @@ -66,7 +67,7 @@ describe('Connector partition checkpoint storage', () => { }) afterEach(async () => { vi.unstubAllEnvs() - await db.delete(workspace).where(eq(workspace.id, owner.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, owner.workspaceId)) await db.delete(organization).where(eq(organization.id, owner.organizationId)) await db.delete(user).where(inArray(user.id, [owner.aliceId, owner.bobId])) }) diff --git a/apps/sim/lib/knowledge/__integration__/connector-permissions.integration.ts b/apps/sim/lib/knowledge/__integration__/connector-permissions.integration.ts index 5788084bcfa..05647c8a586 100644 --- a/apps/sim/lib/knowledge/__integration__/connector-permissions.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/connector-permissions.integration.ts @@ -1,6 +1,7 @@ /** Real PostgreSQL coverage for provider-independent snapshots and administrator-managed grants. */ import { db } from '@sim/db' import { knowledgeConnector, organization, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { seedKnowledgeAclFixture } from '@/lib/knowledge/__integration__/seed-source-access-fixture' @@ -21,7 +22,7 @@ describe('connector-owned permission storage', () => { }) afterAll(async () => { for (const owner of owners) { - await db.delete(workspace).where(eq(workspace.id, owner.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, owner.workspaceId)) await db.delete(organization).where(eq(organization.id, owner.organizationId)) await db.delete(user).where(inArray(user.id, [owner.aliceId, owner.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/connector-persistence-regressions.integration.ts b/apps/sim/lib/knowledge/__integration__/connector-persistence-regressions.integration.ts index e9ead5863f8..5e4a222d58d 100644 --- a/apps/sim/lib/knowledge/__integration__/connector-persistence-regressions.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/connector-persistence-regressions.integration.ts @@ -9,6 +9,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { NextRequest } from 'next/server' @@ -52,7 +53,7 @@ describe('connector source metadata cannot break persistence or API projection', afterAll(async () => { await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await db.$client.end() diff --git a/apps/sim/lib/knowledge/__integration__/connector-save-concurrency.integration.ts b/apps/sim/lib/knowledge/__integration__/connector-save-concurrency.integration.ts index 63d74b6e724..300a146f850 100644 --- a/apps/sim/lib/knowledge/__integration__/connector-save-concurrency.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/connector-save-concurrency.integration.ts @@ -14,6 +14,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNotNull, isNull, sql } from 'drizzle-orm' import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' @@ -103,7 +104,7 @@ describe('independent connector saves in one knowledge base', () => { await db .delete(outboxEvent) .where(sql`${outboxEvent.payload}->>'workspaceId' = ${ids.workspaceId}`) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) }) diff --git a/apps/sim/lib/knowledge/__integration__/connector-upload.integration.ts b/apps/sim/lib/knowledge/__integration__/connector-upload.integration.ts index a2831d2bf6c..ff455edd9ac 100644 --- a/apps/sim/lib/knowledge/__integration__/connector-upload.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/connector-upload.integration.ts @@ -13,6 +13,7 @@ import { workspace, workspaceFiles, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -51,7 +52,7 @@ describe('connector upload crash recovery', () => { vi.restoreAllMocks() if (events.length) await db.delete(outboxEvent).where(inArray(outboxEvent.id, events)) await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await rm(fixtureStorage.root, { recursive: true, force: true }) diff --git a/apps/sim/lib/knowledge/__integration__/directory-sync.integration.ts b/apps/sim/lib/knowledge/__integration__/directory-sync.integration.ts index 357cf09fdb0..dd7eabe1f87 100644 --- a/apps/sim/lib/knowledge/__integration__/directory-sync.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/directory-sync.integration.ts @@ -13,6 +13,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createMockRequest } from '@sim/testing' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' @@ -125,7 +126,7 @@ describe('directory failure visibility in PostgreSQL', () => { .where(eq(knowledgeExternalGroup.workspaceId, ids.workspaceId)) }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where(eq(user.id, ids.aliceId)) await db.delete(user).where(eq(user.id, ids.bobId)) await db.$client.end() diff --git a/apps/sim/lib/knowledge/__integration__/dormant-processing-recovery.integration.ts b/apps/sim/lib/knowledge/__integration__/dormant-processing-recovery.integration.ts index 2ccf293562c..a8f0c0f7df7 100644 --- a/apps/sim/lib/knowledge/__integration__/dormant-processing-recovery.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/dormant-processing-recovery.integration.ts @@ -18,6 +18,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -104,7 +105,7 @@ afterAll(async () => { .delete(outboxEvent) .where(sql`${outboxEvent.payload}->>'knowledgeBaseId' = ${ids.knowledgeBaseId}`) await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/dormant-search-processing.integration.ts b/apps/sim/lib/knowledge/__integration__/dormant-search-processing.integration.ts index 0ed419014ed..2162bd369b8 100644 --- a/apps/sim/lib/knowledge/__integration__/dormant-search-processing.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/dormant-search-processing.integration.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { document, knowledgeBase, organization, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -38,7 +39,7 @@ describe('dormant Search document processing', () => { afterAll(async () => { await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) }) diff --git a/apps/sim/lib/knowledge/__integration__/embedding-insert-batches.integration.ts b/apps/sim/lib/knowledge/__integration__/embedding-insert-batches.integration.ts index 7c47768857e..b73c367f945 100644 --- a/apps/sim/lib/knowledge/__integration__/embedding-insert-batches.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/embedding-insert-batches.integration.ts @@ -13,6 +13,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -57,7 +58,7 @@ describe('bounded embedding insert transactions', () => { await db.$client.unsafe(`DROP TRIGGER IF EXISTS ${triggerName} ON embedding`) await db.$client.unsafe(`DROP FUNCTION IF EXISTS ${triggerName}()`) await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await rm(fixtures.root, { recursive: true, force: true }) diff --git a/apps/sim/lib/knowledge/__integration__/embedding-processing-recovery.integration.ts b/apps/sim/lib/knowledge/__integration__/embedding-processing-recovery.integration.ts index e23caca3fde..d20e1b4ca11 100644 --- a/apps/sim/lib/knowledge/__integration__/embedding-processing-recovery.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/embedding-processing-recovery.integration.ts @@ -14,6 +14,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -72,7 +73,7 @@ describe('embedding progress survives a processing slice', () => { Object.assign(env, previous) if (events.length) await db.delete(outboxEvent).where(inArray(outboxEvent.id, events)) await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await rm(fixtureStorage.root, { recursive: true, force: true }) diff --git a/apps/sim/lib/knowledge/__integration__/excluded-member-documents.integration.ts b/apps/sim/lib/knowledge/__integration__/excluded-member-documents.integration.ts index 79e4b2a111e..2082c8467da 100644 --- a/apps/sim/lib/knowledge/__integration__/excluded-member-documents.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/excluded-member-documents.integration.ts @@ -11,6 +11,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' @@ -183,7 +184,7 @@ describe('excluded member documents retain current source authorization', () => }) afterEach(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) }) diff --git a/apps/sim/lib/knowledge/__integration__/execution-archive-provenance.integration.ts b/apps/sim/lib/knowledge/__integration__/execution-archive-provenance.integration.ts index 3a3332a2569..f6f73706feb 100644 --- a/apps/sim/lib/knowledge/__integration__/execution-archive-provenance.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/execution-archive-provenance.integration.ts @@ -14,6 +14,7 @@ import { workspace, workspaceFiles, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray, sql } from 'drizzle-orm' import JSZip from 'jszip' @@ -173,7 +174,7 @@ afterAll(async () => { } for (const ids of fixtures) { await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/external-file-provenance.integration.ts b/apps/sim/lib/knowledge/__integration__/external-file-provenance.integration.ts index 39c7192f9d2..5eff2d72592 100644 --- a/apps/sim/lib/knowledge/__integration__/external-file-provenance.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/external-file-provenance.integration.ts @@ -5,6 +5,7 @@ import { tmpdir } from 'node:os' import path from 'node:path' import { db } from '@sim/db' import { knowledgeBase, organization, user, workspace, workspaceFiles } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { isPlainRecord } from '@sim/utils/object' import { eq, inArray } from 'drizzle-orm' @@ -132,7 +133,7 @@ afterAll(async () => { validateUrlSpy.mockRestore() for (const ids of fixtures) { await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/filtered-search.integration.ts b/apps/sim/lib/knowledge/__integration__/filtered-search.integration.ts index e08292154f5..e54b80a29c1 100644 --- a/apps/sim/lib/knowledge/__integration__/filtered-search.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/filtered-search.integration.ts @@ -9,6 +9,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -123,7 +124,7 @@ describe.each([384, 768, 1024, 1536, 3072] as const)( }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where(eq(user.id, ids.aliceId)) await db.delete(user).where(eq(user.id, ids.bobId)) }) diff --git a/apps/sim/lib/knowledge/__integration__/github-member.integration.ts b/apps/sim/lib/knowledge/__integration__/github-member.integration.ts index 7829bd571eb..03dcd80b13d 100644 --- a/apps/sim/lib/knowledge/__integration__/github-member.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/github-member.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * Fixture-backed GitHub API with real PostgreSQL, credential policy/token resolution, * connector registry, member sync, storage, chunking, and application authorization. @@ -549,7 +550,7 @@ describe('fixture-backed GitHub member search in PostgreSQL', () => { ) ) ) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) vi.restoreAllMocks() diff --git a/apps/sim/lib/knowledge/__integration__/gitlab-federated.integration.ts b/apps/sim/lib/knowledge/__integration__/gitlab-federated.integration.ts index ac33889af41..b217bc67d93 100644 --- a/apps/sim/lib/knowledge/__integration__/gitlab-federated.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/gitlab-federated.integration.ts @@ -19,6 +19,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { isPlainRecord } from '@sim/utils/object' @@ -284,7 +285,7 @@ describe.skipIf(!fixtureFile)('federated self-hosted GitLab Search', () => { })(), (async () => { if (!ids) return - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId, ...simUserIds])) })(), diff --git a/apps/sim/lib/knowledge/__integration__/gitlab-live.integration.ts b/apps/sim/lib/knowledge/__integration__/gitlab-live.integration.ts index 974768e2295..b1b18e3ffff 100644 --- a/apps/sim/lib/knowledge/__integration__/gitlab-live.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/gitlab-live.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * Opt-in self-hosted GitLab test. GITLAB_LIVE_FIXTURE_FILE contains {url, token, * auditorToken?} for a disposable localhost HTTPS instance. The administrator @@ -430,7 +431,7 @@ describe.skipIf(!fixtureFile)('live self-hosted GitLab ingestion and permission throw error } } - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId, ...extraSimIds])) } diff --git a/apps/sim/lib/knowledge/__integration__/gitlab-workspace.integration.ts b/apps/sim/lib/knowledge/__integration__/gitlab-workspace.integration.ts index 2774744beb5..ab90381eafe 100644 --- a/apps/sim/lib/knowledge/__integration__/gitlab-workspace.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/gitlab-workspace.integration.ts @@ -15,6 +15,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNull } from 'drizzle-orm' import { afterAll, beforeAll, expect, it, vi } from 'vitest' @@ -112,7 +113,7 @@ beforeAll(async () => { afterAll(async () => { vi.restoreAllMocks() - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await rm(fixture.root, { recursive: true, force: true }) diff --git a/apps/sim/lib/knowledge/__integration__/gmail-member.integration.ts b/apps/sim/lib/knowledge/__integration__/gmail-member.integration.ts index af955d4df2b..cc07d015cda 100644 --- a/apps/sim/lib/knowledge/__integration__/gmail-member.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/gmail-member.integration.ts @@ -18,6 +18,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' @@ -307,7 +308,7 @@ describe('Gmail member ingestion and ACLs in PostgreSQL (provider fixtures)', () } } finally { storageKeys.clear() - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } }) diff --git a/apps/sim/lib/knowledge/__integration__/google-calendar-member.integration.ts b/apps/sim/lib/knowledge/__integration__/google-calendar-member.integration.ts index 4d406455565..574c67c19a3 100644 --- a/apps/sim/lib/knowledge/__integration__/google-calendar-member.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/google-calendar-member.integration.ts @@ -19,6 +19,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNull } from 'drizzle-orm' import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' @@ -339,7 +340,7 @@ describe('Google Calendar member indexing and authorization in PostgreSQL', () = if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) throw error }) } finally { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where( inArray( user.id, diff --git a/apps/sim/lib/knowledge/__integration__/google-drive-live.integration.ts b/apps/sim/lib/knowledge/__integration__/google-drive-live.integration.ts index 84436ace304..068c629441f 100644 --- a/apps/sim/lib/knowledge/__integration__/google-drive-live.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/google-drive-live.integration.ts @@ -22,6 +22,7 @@ import { workspace, workspaceFiles, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -251,7 +252,7 @@ describe.skipIf(!keyPath || !folderId || !ownerEmail)( } }) } - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where(eq(user.id, ids.aliceId)) await db.delete(user).where(eq(user.id, ids.bobId)) } diff --git a/apps/sim/lib/knowledge/__integration__/google-drive-shortcuts.integration.ts b/apps/sim/lib/knowledge/__integration__/google-drive-shortcuts.integration.ts index 80b569631bf..eeffefbad5b 100644 --- a/apps/sim/lib/knowledge/__integration__/google-drive-shortcuts.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/google-drive-shortcuts.integration.ts @@ -13,6 +13,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray } from 'drizzle-orm' import { PDFDocument, StandardFonts } from 'pdf-lib' @@ -216,7 +217,7 @@ describe('Drive shortcuts through indexing and search', () => { vi.stubGlobal('fetch', providerFetch) }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await rm(fixture.storageRoot, { recursive: true, force: true }) diff --git a/apps/sim/lib/knowledge/__integration__/jira-member.integration.ts b/apps/sim/lib/knowledge/__integration__/jira-member.integration.ts index b3ae46cd07f..8672778309b 100644 --- a/apps/sim/lib/knowledge/__integration__/jira-member.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/jira-member.integration.ts @@ -20,6 +20,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNull } from 'drizzle-orm' import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' @@ -308,7 +309,7 @@ describe('Jira member indexing and authorization in PostgreSQL', () => { if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) throw error }) } finally { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where( inArray( user.id, diff --git a/apps/sim/lib/knowledge/__integration__/kb-block-search.integration.ts b/apps/sim/lib/knowledge/__integration__/kb-block-search.integration.ts index 7ebef980cfb..294ee0ff1c4 100644 --- a/apps/sim/lib/knowledge/__integration__/kb-block-search.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/kb-block-search.integration.ts @@ -2,6 +2,7 @@ import type { Principal } from '@sim/auth/principal' import { db } from '@sim/db' import { document, embedding, knowledgeBase, organization, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -79,7 +80,7 @@ describe('API-key KB block fan-out', () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await db.$client.end() diff --git a/apps/sim/lib/knowledge/__integration__/knowledge-base-deletion.integration.ts b/apps/sim/lib/knowledge/__integration__/knowledge-base-deletion.integration.ts index 07315c48fc6..74ffcefd1e8 100644 --- a/apps/sim/lib/knowledge/__integration__/knowledge-base-deletion.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/knowledge-base-deletion.integration.ts @@ -9,6 +9,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNull, sql } from 'drizzle-orm' import { afterEach, describe, expect, it } from 'vitest' @@ -59,7 +60,7 @@ describe('knowledge base deletion in PostgreSQL', () => { afterEach(async () => { for (const ids of fixtures.splice(0)) { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/knowledge-projection.integration.ts b/apps/sim/lib/knowledge/__integration__/knowledge-projection.integration.ts index 3b9f4f8a174..cc634808320 100644 --- a/apps/sim/lib/knowledge/__integration__/knowledge-projection.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/knowledge-projection.integration.ts @@ -17,6 +17,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray, sql } from 'drizzle-orm' import postgres from 'postgres' @@ -116,7 +117,7 @@ beforeEach(async () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await projector?.end() diff --git a/apps/sim/lib/knowledge/__integration__/listing-continuation.integration.ts b/apps/sim/lib/knowledge/__integration__/listing-continuation.integration.ts index 70ef7114fc7..78a4182d587 100644 --- a/apps/sim/lib/knowledge/__integration__/listing-continuation.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/listing-continuation.integration.ts @@ -19,6 +19,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { toNumberOrNull } from '@sim/utils/coerce' import { generateId } from '@sim/utils/id' import { toArray, toRecord } from '@sim/utils/object' @@ -150,7 +151,7 @@ describe('durable source and member cycles in PostgreSQL', () => { })) }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where(eq(user.id, ids.aliceId)) await db.delete(user).where(eq(user.id, ids.bobId)) await rm(fixture.storageRoot, { recursive: true, force: true }) diff --git a/apps/sim/lib/knowledge/__integration__/member-document-lifecycle.integration.ts b/apps/sim/lib/knowledge/__integration__/member-document-lifecycle.integration.ts index 801274d195a..f0873f9196a 100644 --- a/apps/sim/lib/knowledge/__integration__/member-document-lifecycle.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/member-document-lifecycle.integration.ts @@ -9,6 +9,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNotNull, sql } from 'drizzle-orm' import { @@ -63,7 +64,7 @@ describe('member document lifecycle in PostgreSQL', () => { }) afterEach(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) }) diff --git a/apps/sim/lib/knowledge/__integration__/member-scope-renewal.integration.ts b/apps/sim/lib/knowledge/__integration__/member-scope-renewal.integration.ts index 0a35cf64cbe..72c4492919e 100644 --- a/apps/sim/lib/knowledge/__integration__/member-scope-renewal.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/member-scope-renewal.integration.ts @@ -7,6 +7,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, lt, sql } from 'drizzle-orm' import { afterAll, afterEach, beforeEach, describe, expect, it } from 'vitest' @@ -31,7 +32,7 @@ describe('member observation renewal by access scope in PostgreSQL', () => { }) afterEach(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) }) diff --git a/apps/sim/lib/knowledge/__integration__/ocr-input-failures.integration.ts b/apps/sim/lib/knowledge/__integration__/ocr-input-failures.integration.ts index 3dd20af7932..476bc08859c 100644 --- a/apps/sim/lib/knowledge/__integration__/ocr-input-failures.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/ocr-input-failures.integration.ts @@ -14,6 +14,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { sha256Hex } from '@sim/security/hash' import { generateId } from '@sim/utils/id' import { eq, inArray, sql } from 'drizzle-orm' @@ -83,7 +84,7 @@ describe('OCR input failures stop without partial indexing or futile retries', ( await db.delete(rateLimitBucket).where(inArray(rateLimitBucket.key, capacityKeys)) for (const ids of seeded) { await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/processing-lock-scope.integration.ts b/apps/sim/lib/knowledge/__integration__/processing-lock-scope.integration.ts index fb793b32330..b05608e1a9a 100644 --- a/apps/sim/lib/knowledge/__integration__/processing-lock-scope.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/processing-lock-scope.integration.ts @@ -8,6 +8,7 @@ import { tmpdir } from 'node:os' import path from 'node:path' import { db } from '@sim/db' import { document, embedding, knowledgeBase, organization, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import postgres from 'postgres' @@ -70,7 +71,7 @@ describe('document processing commit lock scope', () => { afterAll(async () => { vi.restoreAllMocks() await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await rm(fixtures.root, { recursive: true, force: true }) diff --git a/apps/sim/lib/knowledge/__integration__/provider-processing-recovery.integration.ts b/apps/sim/lib/knowledge/__integration__/provider-processing-recovery.integration.ts index a3b567598f1..44b54da3ada 100644 --- a/apps/sim/lib/knowledge/__integration__/provider-processing-recovery.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/provider-processing-recovery.integration.ts @@ -18,6 +18,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { sha256Hex } from '@sim/security/hash' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' @@ -95,7 +96,7 @@ describe('provider throttling resumes the shared indexing pipeline', () => { await db.delete(rateLimitBucket).where(inArray(rateLimitBucket.key, capacityKeys)) for (const ids of seeded) { await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/providers-live.integration.ts b/apps/sim/lib/knowledge/__integration__/providers-live.integration.ts index c65f83a8fdc..d85537c177d 100644 --- a/apps/sim/lib/knowledge/__integration__/providers-live.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/providers-live.integration.ts @@ -18,6 +18,7 @@ import { workspaceBYOKKeys, workspaceFiles, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createLogger, LogLevel } from '@sim/logger' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' @@ -107,7 +108,7 @@ describe.skipIf(!credentialsFile)('real embedding and scanned PDF providers', () .from(workspaceFiles) .where(eq(workspaceFiles.workspaceId, ids.workspaceId)) for (const file of files) await deleteFile({ key: file.key, context: 'knowledge-base' }) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } if (fixtureStorage.root) await rm(fixtureStorage.root, { recursive: true, force: true }) diff --git a/apps/sim/lib/knowledge/__integration__/purged-detach-reservation.integration.ts b/apps/sim/lib/knowledge/__integration__/purged-detach-reservation.integration.ts index 88d9c24e09d..23d77f6b79d 100644 --- a/apps/sim/lib/knowledge/__integration__/purged-detach-reservation.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/purged-detach-reservation.integration.ts @@ -14,6 +14,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNull, sql } from 'drizzle-orm' import { afterAll, describe, expect, it } from 'vitest' @@ -138,7 +139,7 @@ afterAll(async () => { ) ) await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/scale.integration.ts b/apps/sim/lib/knowledge/__integration__/scale.integration.ts index a9bebc84d4b..31d2d2a3d04 100644 --- a/apps/sim/lib/knowledge/__integration__/scale.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/scale.integration.ts @@ -1,6 +1,7 @@ import { readFileSync, statSync, writeFileSync } from 'node:fs' import { db } from '@sim/db' import { document, embedding, knowledgeConnector, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createLogger } from '@sim/logger' import { toStringOrNull } from '@sim/utils/coerce' import { toArray, toRecord } from '@sim/utils/object' @@ -259,7 +260,7 @@ describe.skipIf(!enabled)('knowledge scale: isolated real PostgreSQL, no provide try { await snapshot('final') if (!keepDatabase) { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } } finally { diff --git a/apps/sim/lib/knowledge/__integration__/search-index-policy.integration.ts b/apps/sim/lib/knowledge/__integration__/search-index-policy.integration.ts index 75880eba264..0a286b3d43d 100644 --- a/apps/sim/lib/knowledge/__integration__/search-index-policy.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/search-index-policy.integration.ts @@ -2,6 +2,7 @@ import type { Principal } from '@sim/auth/principal' import { db } from '@sim/db' import { document, folder, knowledgeBase, permissions, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -107,7 +108,7 @@ describe('canonical search knowledge-base policy', () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(user).where(eq(user.id, ids.aliceId)) await db.delete(user).where(eq(user.id, ids.bobId)) vi.unstubAllGlobals() diff --git a/apps/sim/lib/knowledge/__integration__/search-latency.integration.ts b/apps/sim/lib/knowledge/__integration__/search-latency.integration.ts index 270e642001e..548a8dbc393 100644 --- a/apps/sim/lib/knowledge/__integration__/search-latency.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/search-latency.integration.ts @@ -14,6 +14,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createLogger, Logger } from '@sim/logger' import { generateId } from '@sim/utils/id' import { eq, inArray, sql } from 'drizzle-orm' @@ -712,7 +713,7 @@ describe.skipIf(!enabled)('Knowledge search latency on a realistic indexed corpu for (const fixture of process.env.KNOWLEDGE_SEARCH_PERFORMANCE_KEEP_DATABASE === 'true' ? [] : [ids, unrelated, fullWidthFixture]) { - await db.delete(workspace).where(eq(workspace.id, fixture.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, fixture.workspaceId)) await db.delete(organization).where(eq(organization.id, fixture.organizationId)) await db.delete(user).where(eq(user.id, fixture.aliceId)) await db.delete(user).where(eq(user.id, fixture.bobId)) diff --git a/apps/sim/lib/knowledge/__integration__/search-reference-batching.integration.ts b/apps/sim/lib/knowledge/__integration__/search-reference-batching.integration.ts index a08d83b195a..a7b0e91f100 100644 --- a/apps/sim/lib/knowledge/__integration__/search-reference-batching.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/search-reference-batching.integration.ts @@ -6,6 +6,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -53,7 +54,7 @@ describe('batched search reference reads', () => { afterAll(async () => { vi.restoreAllMocks() - await db.delete(workspace).where(eq(workspace.id, fixture.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, fixture.workspaceId)) await db.delete(organization).where(eq(organization.id, fixture.organizationId)) await db.delete(user).where(inArray(user.id, [fixture.aliceId, fixture.bobId])) await db.$client.end() diff --git a/apps/sim/lib/knowledge/__integration__/search-source-pagination.integration.ts b/apps/sim/lib/knowledge/__integration__/search-source-pagination.integration.ts index 42e7b124f87..263e56718e6 100644 --- a/apps/sim/lib/knowledge/__integration__/search-source-pagination.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/search-source-pagination.integration.ts @@ -8,6 +8,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -61,7 +62,7 @@ beforeAll(async () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(eq(user.id, ids.aliceId)) await db.delete(user).where(eq(user.id, ids.bobId)) diff --git a/apps/sim/lib/knowledge/__integration__/search-source-progress.integration.ts b/apps/sim/lib/knowledge/__integration__/search-source-progress.integration.ts index e4580b084e3..1f58f05f0a3 100644 --- a/apps/sim/lib/knowledge/__integration__/search-source-progress.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/search-source-progress.integration.ts @@ -9,6 +9,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -69,7 +70,7 @@ beforeAll(async () => { ) }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(eq(user.id, ids.aliceId)) await db.delete(user).where(eq(user.id, ids.bobId)) @@ -121,7 +122,7 @@ describe('retired Search document admission', () => { }) }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, fixture.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, fixture.workspaceId)) await db.delete(organization).where(eq(organization.id, fixture.organizationId)) await db.delete(user).where(inArray(user.id, [fixture.aliceId, fixture.bobId])) }) @@ -245,7 +246,7 @@ describe('connector document filename search and document sets', () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, fixture.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, fixture.workspaceId)) await db.delete(organization).where(eq(organization.id, fixture.organizationId)) await db.delete(user).where(eq(user.id, fixture.aliceId)) await db.delete(user).where(eq(user.id, fixture.bobId)) @@ -445,7 +446,7 @@ describe('intentional skips and genuine failures across document reads', () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, fixture.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, fixture.workspaceId)) await db.delete(organization).where(eq(organization.id, fixture.organizationId)) await db.delete(user).where(eq(user.id, fixture.aliceId)) await db.delete(user).where(eq(user.id, fixture.bobId)) diff --git a/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts b/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts index eee347e4058..387054bda41 100644 --- a/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts @@ -10,6 +10,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, isNull } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -93,8 +94,8 @@ describe('Search source identity and concurrent creation', () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) - await db.delete(workspace).where(eq(workspace.id, other.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, other.workspaceId)) for (const id of [ids.aliceId, ids.bobId, other.aliceId, other.bobId]) { await db.delete(user).where(eq(user.id, id)) } @@ -177,7 +178,7 @@ describe('Search source identity and concurrent creation', () => { expect(policies).toHaveLength(0) } finally { await db.delete(permissions).where(eq(permissions.entityId, created.id)) - await db.delete(workspace).where(eq(workspace.id, created.id)) + await deleteWorkspaceFixture(db, eq(workspace.id, created.id)) } }) diff --git a/apps/sim/lib/knowledge/__integration__/seed-source-access-fixture.ts b/apps/sim/lib/knowledge/__integration__/seed-source-access-fixture.ts index 66ee5364efc..28441370e74 100644 --- a/apps/sim/lib/knowledge/__integration__/seed-source-access-fixture.ts +++ b/apps/sim/lib/knowledge/__integration__/seed-source-access-fixture.ts @@ -13,9 +13,9 @@ import { organization, permissions, user, - workspace, } from '@sim/db/schema' import { assertDisposableTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' @@ -79,7 +79,7 @@ export async function seedKnowledgeAclFixture( name: 'ACL integration organization', slug: ids.organizationId, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, organizationId: ids.organizationId, name: 'ACL integration fixture', diff --git a/apps/sim/lib/knowledge/__integration__/slack-empty-threads.integration.ts b/apps/sim/lib/knowledge/__integration__/slack-empty-threads.integration.ts index a27f136fb84..a0da7d35c56 100644 --- a/apps/sim/lib/knowledge/__integration__/slack-empty-threads.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/slack-empty-threads.integration.ts @@ -12,6 +12,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -174,7 +175,7 @@ describe('Slack empty threads through sync jobs, indexing and search', () => { }) }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) await rm(fixture.storageRoot, { recursive: true, force: true }) diff --git a/apps/sim/lib/knowledge/__integration__/storage-accounting.integration.ts b/apps/sim/lib/knowledge/__integration__/storage-accounting.integration.ts index 41d9e626199..fc582b2fd3d 100644 --- a/apps/sim/lib/knowledge/__integration__/storage-accounting.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/storage-accounting.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** Real PostgreSQL coverage for storage ownership changes and document lifecycle accounting. */ import { execFile } from 'node:child_process' @@ -128,7 +129,7 @@ afterAll(async () => { ) ) await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/storage-cleanup.integration.ts b/apps/sim/lib/knowledge/__integration__/storage-cleanup.integration.ts index cd45fc96672..205f2863e7e 100644 --- a/apps/sim/lib/knowledge/__integration__/storage-cleanup.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/storage-cleanup.integration.ts @@ -13,6 +13,7 @@ import { workspace, workspaceFiles, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { desc, eq, inArray, sql } from 'drizzle-orm' @@ -57,7 +58,7 @@ describe('knowledge backing storage cleanup in PostgreSQL', () => { if (events.length) await db.delete(outboxEvent).where(inArray(outboxEvent.id, events)) for (const ids of fixtures) { await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/stored-document-recovery.integration.ts b/apps/sim/lib/knowledge/__integration__/stored-document-recovery.integration.ts index 50b29de0fc2..7fe905f0051 100644 --- a/apps/sim/lib/knowledge/__integration__/stored-document-recovery.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/stored-document-recovery.integration.ts @@ -14,6 +14,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest' @@ -174,7 +175,7 @@ afterAll(async () => { .delete(outboxEvent) .where(sql`${outboxEvent.payload}->>'knowledgeBaseId' = ${ids.knowledgeBaseId}`) await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/upload-read-provenance.integration.ts b/apps/sim/lib/knowledge/__integration__/upload-read-provenance.integration.ts index 16733d0e868..72b0687f2cb 100644 --- a/apps/sim/lib/knowledge/__integration__/upload-read-provenance.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/upload-read-provenance.integration.ts @@ -15,6 +15,7 @@ import { workspaceFileSecretProvenance, workspaceFiles, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -130,7 +131,7 @@ afterAll(async () => { for (const ids of fixtures) { await db.delete(auditLog).where(eq(auditLog.workspaceId, ids.workspaceId)) await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/user-document-visibility.integration.ts b/apps/sim/lib/knowledge/__integration__/user-document-visibility.integration.ts index 4e551f06cfb..541c17950c2 100644 --- a/apps/sim/lib/knowledge/__integration__/user-document-visibility.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/user-document-visibility.integration.ts @@ -1,6 +1,7 @@ /** Real PostgreSQL coverage for the probe that keeps a user's visible documents from being skipped. */ import { db } from '@sim/db' import { document, knowledgeConnector, organization, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterAll, afterEach, beforeEach, describe, expect, it } from 'vitest' @@ -28,7 +29,7 @@ describe('Visible user documents in PostgreSQL', () => { }) }) afterEach(async () => { - await db.delete(workspace).where(eq(workspace.id, owner.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, owner.workspaceId)) await db.delete(organization).where(eq(organization.id, owner.organizationId)) await db.delete(user).where(inArray(user.id, [owner.aliceId, owner.bobId])) }) diff --git a/apps/sim/lib/knowledge/__integration__/workspace-import.integration.ts b/apps/sim/lib/knowledge/__integration__/workspace-import.integration.ts index 113eaa599e0..056b3482383 100644 --- a/apps/sim/lib/knowledge/__integration__/workspace-import.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/workspace-import.integration.ts @@ -17,6 +17,7 @@ import { workspace, workspaceFiles, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -130,7 +131,7 @@ afterAll(async () => { for (const ids of fixtures) { await db.delete(auditLog).where(eq(auditLog.workspaceId, ids.workspaceId)) await db.delete(knowledgeBase).where(eq(knowledgeBase.id, ids.knowledgeBaseId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/knowledge/__integration__/workspace-kb-document-access.integration.ts b/apps/sim/lib/knowledge/__integration__/workspace-kb-document-access.integration.ts index 85be5ca3113..01f456718cb 100644 --- a/apps/sim/lib/knowledge/__integration__/workspace-kb-document-access.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/workspace-kb-document-access.integration.ts @@ -27,6 +27,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -141,7 +142,7 @@ describe.each([ }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) }) @@ -356,7 +357,7 @@ describe('a workspace source whose reader must be proven live', () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(credentialGroup).where(eq(credentialGroup.id, groupId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) diff --git a/apps/sim/lib/knowledge/__integration__/workspace-lifecycle.integration.ts b/apps/sim/lib/knowledge/__integration__/workspace-lifecycle.integration.ts index 817f3903db8..38c5c50070b 100644 --- a/apps/sim/lib/knowledge/__integration__/workspace-lifecycle.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/workspace-lifecycle.integration.ts @@ -8,6 +8,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterEach, describe, expect, it, vi } from 'vitest' @@ -33,6 +34,7 @@ import { archiveWorkspace } from '@/lib/workspaces/lifecycle' describe('workspace knowledge lifecycle in PostgreSQL', () => { const fixtures: ReturnType[] = [] + const retainedEnvironments = new Map() async function seed() { const ids = createKnowledgeAclFixtureIds() @@ -53,7 +55,12 @@ describe('workspace knowledge lifecycle in PostgreSQL', () => { afterEach(async () => { for (const ids of fixtures.splice(0)) { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + const retained = retainedEnvironments.get(ids.workspaceId) + await deleteWorkspaceFixture( + db, + inArray(workspace.id, retained ? [ids.workspaceId, retained] : [ids.workspaceId]) + ) + retainedEnvironments.delete(ids.workspaceId) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } @@ -64,6 +71,16 @@ describe('workspace knowledge lifecycle in PostgreSQL', () => { async (alreadyArchived) => { const ids = await seed() const other = await seed() + const retainedId = generateId() + retainedEnvironments.set(ids.workspaceId, retainedId) + await insertWorkspaceFixture(db, { + id: retainedId, + name: 'Retained active environment', + ownerId: ids.aliceId, + billedAccountUserId: ids.aliceId, + organizationId: ids.organizationId, + forkedFromWorkspaceId: ids.workspaceId, + }) const previousArchive = new Date('2025-01-01T00:00:00.000Z') if (alreadyArchived) { await db @@ -118,7 +135,7 @@ describe('workspace knowledge lifecycle in PostgreSQL', () => { it('hard deletion cascades to KBs, documents, and connectors', async () => { const ids = await seed() - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) expect( await db diff --git a/apps/sim/lib/logs/execution/completion-ledger-order.integration.ts b/apps/sim/lib/logs/execution/completion-ledger-order.integration.ts index 111e618a82d..f1c41610836 100644 --- a/apps/sim/lib/logs/execution/completion-ledger-order.integration.ts +++ b/apps/sim/lib/logs/execution/completion-ledger-order.integration.ts @@ -11,6 +11,7 @@ import { workflowExecutionSnapshots, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createDeferred } from '@sim/testing' import { generateId } from '@sim/utils/id' import { eq, sql } from 'drizzle-orm' @@ -84,7 +85,7 @@ beforeAll(async () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: ids.workspace, name: 'Ledger Order', ownerId: ids.owner, @@ -107,7 +108,7 @@ afterAll(async () => { await db .delete(workflowExecutionSnapshots) .where(eq(workflowExecutionSnapshots.workflowId, ids.workflow)) - await db.delete(workspace).where(eq(workspace.id, ids.workspace)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspace)) await db.delete(user).where(eq(user.id, ids.owner)) }) diff --git a/apps/sim/lib/logs/execution/start-execution.integration.ts b/apps/sim/lib/logs/execution/start-execution.integration.ts index ea835d3d966..0db51eefcd7 100644 --- a/apps/sim/lib/logs/execution/start-execution.integration.ts +++ b/apps/sim/lib/logs/execution/start-execution.integration.ts @@ -10,6 +10,7 @@ import { workflowExecutionSnapshots, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -84,7 +85,7 @@ beforeAll(async () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: ids.workspace, name: 'Start Log', ownerId: ids.owner, @@ -107,7 +108,7 @@ afterAll(async () => { await db .delete(workflowExecutionSnapshots) .where(eq(workflowExecutionSnapshots.workflowId, ids.workflow)) - await db.delete(workspace).where(eq(workspace.id, ids.workspace)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspace)) await db.delete(user).where(eq(user.id, ids.owner)) }) diff --git a/apps/sim/lib/mothership/agent-cli/workbench-file-provenance.integration.ts b/apps/sim/lib/mothership/agent-cli/workbench-file-provenance.integration.ts index 733c078aada..2fbc47f2f47 100644 --- a/apps/sim/lib/mothership/agent-cli/workbench-file-provenance.integration.ts +++ b/apps/sim/lib/mothership/agent-cli/workbench-file-provenance.integration.ts @@ -4,6 +4,7 @@ import { AuditAction } from '@sim/audit' import { db } from '@sim/db' import { auditLog, organization, user, workspace } from '@sim/db/schema' import { readTestRedisUrl } from '@sim/db/testing/test-infrastructure' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { flushMacrotask } from '@sim/testing/helpers/async' import { redisConfigMock, redisConfigMockFns } from '@sim/testing/mocks/redis-config.mock' import { generateShortId } from '@sim/utils/id' @@ -101,7 +102,7 @@ beforeAll(async () => { createdAt: new Date(), updatedAt: new Date(), }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: fixtureWorkspaceId, name: 'Receipt test workspace', ownerId: fixtureUserId, @@ -143,7 +144,7 @@ afterAll(async () => { } if (historyKeys.size > 0) await redis.del(...historyKeys) await db.delete(auditLog).where(eq(auditLog.workspaceId, fixtureWorkspaceId)) - await db.delete(workspace).where(eq(workspace.id, fixtureWorkspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, fixtureWorkspaceId)) await db.delete(user).where(eq(user.id, fixtureUserId)) } finally { restoreAuditObservation?.() diff --git a/apps/sim/lib/mothership/async-runs/orphaned-runs.integration.ts b/apps/sim/lib/mothership/async-runs/orphaned-runs.integration.ts index 1a51dd8d062..eae1ffdb01b 100644 --- a/apps/sim/lib/mothership/async-runs/orphaned-runs.integration.ts +++ b/apps/sim/lib/mothership/async-runs/orphaned-runs.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * Settlement of Chat runs that no controller owns, against real PostgreSQL and Redis: * the chat stream lock, the replay buffer keys, the run and chat rows, and the Stop @@ -111,7 +112,7 @@ describe.runIf(Boolean(redisUrl))('Chat runs no controller owns', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Orphaned run fixture', ownerId: userId, @@ -129,7 +130,7 @@ describe.runIf(Boolean(redisUrl))('Chat runs no controller owns', () => { afterAll(async () => { if (chatIds.length) await db.delete(copilotChats).where(inArray(copilotChats.id, chatIds)) await db.delete(permissions).where(eq(permissions.userId, userId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/lib/mothership/request/session/recovery-storm.integration.ts b/apps/sim/lib/mothership/request/session/recovery-storm.integration.ts index bd7f34718cf..2319dad203a 100644 --- a/apps/sim/lib/mothership/request/session/recovery-storm.integration.ts +++ b/apps/sim/lib/mothership/request/session/recovery-storm.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * How often reconnecting to an orphaned Chat run re-POSTs it to the worker, against real * Redis and PostgreSQL. The production reconnect route, stream recovery, chat lifecycle @@ -297,7 +298,7 @@ describe.runIf(Boolean(redisUrl))('reconnecting to an orphaned Chat run', () => createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Recovery storm fixture', ownerId: userId, @@ -323,7 +324,7 @@ describe.runIf(Boolean(redisUrl))('reconnecting to an orphaned Chat run', () => await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) } await db.delete(permissions).where(eq(permissions.userId, userId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) await closeRedisConnection() }) diff --git a/apps/sim/lib/mothership/request/session/replay-budget.integration.ts b/apps/sim/lib/mothership/request/session/replay-budget.integration.ts index 6eab2cdd23b..8735d6b78aa 100644 --- a/apps/sim/lib/mothership/request/session/replay-budget.integration.ts +++ b/apps/sim/lib/mothership/request/session/replay-budget.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * The leased Chat stream writer and its replay-budget failure path against real * Redis and PostgreSQL. A local HTTP server stands in for the worker's abort @@ -479,7 +480,7 @@ describe.runIf(Boolean(redisUrl))('a turn whose stream exhausts its replay budge createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Replay budget fixture', ownerId: userId, @@ -502,7 +503,7 @@ describe.runIf(Boolean(redisUrl))('a turn whose stream exhausts its replay budge afterAll(async () => { await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) await db.delete(permissions).where(eq(permissions.userId, userId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/lib/mothership/request/session/replay-gap.integration.ts b/apps/sim/lib/mothership/request/session/replay-gap.integration.ts index f9e8be62fe5..e9d23055b67 100644 --- a/apps/sim/lib/mothership/request/session/replay-gap.integration.ts +++ b/apps/sim/lib/mothership/request/session/replay-gap.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * Reconnects that Sim's replay ring can no longer serve, against real Redis and * PostgreSQL through the production reconnect route. A local HTTP server stands in for @@ -154,7 +155,7 @@ describe.runIf(Boolean(redisUrl))('reconnects past the replay ring', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Replay gap fixture', ownerId: userId, @@ -183,7 +184,7 @@ describe.runIf(Boolean(redisUrl))('reconnects past the replay ring', () => { await db.delete(copilotRuns).where(eq(copilotRuns.chatId, chatId)) await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) await db.delete(permissions).where(eq(permissions.userId, userId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) await closeRedisConnection() }) diff --git a/apps/sim/lib/mothership/request/session/stream-recovery.integration.ts b/apps/sim/lib/mothership/request/session/stream-recovery.integration.ts index 1665d56a39e..20832d77bff 100644 --- a/apps/sim/lib/mothership/request/session/stream-recovery.integration.ts +++ b/apps/sim/lib/mothership/request/session/stream-recovery.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * Recovery of a Chat run whose Sim controller died after its replay ring lost its * head, against real Redis and PostgreSQL through the production reconnect route and @@ -195,7 +196,7 @@ describe.runIf(Boolean(redisUrl))('recovering a run whose ring lost its head', ( createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Stream recovery fixture', ownerId: userId, @@ -221,7 +222,7 @@ describe.runIf(Boolean(redisUrl))('recovering a run whose ring lost its head', ( await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) } await db.delete(permissions).where(eq(permissions.userId, userId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) await closeRedisConnection() }) diff --git a/apps/sim/lib/mothership/request/tools/workflow-client-settlement.integration.ts b/apps/sim/lib/mothership/request/tools/workflow-client-settlement.integration.ts index de69ea80969..a36093b386a 100644 --- a/apps/sim/lib/mothership/request/tools/workflow-client-settlement.integration.ts +++ b/apps/sim/lib/mothership/request/tools/workflow-client-settlement.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * A browser claims a Chat workflow tool, the execute route runs it, and the browser may never * report back (tab closed, network lost, beacon dropped). Runs against real PostgreSQL and Redis: @@ -87,7 +88,7 @@ describe.runIf(Boolean(redisUrl))('settled client-claimed workflow tools', () => createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Workflow settlement fixture', ownerId: userId, @@ -130,7 +131,7 @@ describe.runIf(Boolean(redisUrl))('settled client-claimed workflow tools', () => .delete(workflowExecutionSnapshots) .where(inArray(workflowExecutionSnapshots.id, snapshotIds)) await db.delete(workflow).where(eq(workflow.id, workflowId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/lib/mothership/tasks/application/prepare-wake.integration.ts b/apps/sim/lib/mothership/tasks/application/prepare-wake.integration.ts index 5cf7636e426..d86a9232014 100644 --- a/apps/sim/lib/mothership/tasks/application/prepare-wake.integration.ts +++ b/apps/sim/lib/mothership/tasks/application/prepare-wake.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * How sim answers the worker's retry of a task wake, against real PostgreSQL and Redis: the * wake route, the chat stream lock, and the run records are production code. Only `after` is @@ -67,7 +68,7 @@ describe.runIf(Boolean(redisUrl))('task wake retries', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Task wake fixture', ownerId: userId, @@ -88,7 +89,7 @@ describe.runIf(Boolean(redisUrl))('task wake retries', () => { await db.delete(copilotChats).where(inArray(copilotChats.id, chatIds)) } await db.delete(permissions).where(eq(permissions.userId, userId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index e7fe2cf21d1..5117e1ca408 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -29,7 +29,7 @@ import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' import postgres from 'postgres' -import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import { removeUserFromOrganization } from '@/lib/billing/organizations/membership' import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' import { @@ -61,6 +61,14 @@ beforeEach(() => { vi.stubEnv('PROJECT_API_ENABLED', 'true') }) +let restoreEnforcement = false +beforeAll(async () => { + const rows = await db.execute( + sql`SELECT 1 FROM pg_trigger WHERE tgname = 'project_contract_check' AND tgrelid = 'public.workspace'::regclass` + ) + restoreEnforcement = rows.length > 0 +}) + const users: string[] = [] const organizations: string[] = [] const environments: string[] = [] @@ -192,6 +200,22 @@ afterAll(async () => { if (organizations.length) await db.delete(organization).where(inArray(organization.id, organizations)) if (users.length) await db.delete(user).where(inArray(user.id, users)) + if (restoreEnforcement) { + const client = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => undefined }) + try { + await client.unsafe( + await readFile( + new URL( + '../../../../../packages/db/migrations/0394_project_membership_enforcement.sql', + import.meta.url + ), + 'utf8' + ) + ) + } finally { + await client.end() + } + } }) describe('Project foundation at the database and application boundary', () => { diff --git a/apps/sim/lib/selectors/__integration__/copilot-nested-selectors.integration.ts b/apps/sim/lib/selectors/__integration__/copilot-nested-selectors.integration.ts index 8a8251b4165..572621ba952 100644 --- a/apps/sim/lib/selectors/__integration__/copilot-nested-selectors.integration.ts +++ b/apps/sim/lib/selectors/__integration__/copilot-nested-selectors.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** Chat's in-process CLI reaching selectors whose options are owned by another domain. */ import { db } from '@sim/db' @@ -46,7 +47,7 @@ describe('Copilot selectors backed by another domain', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Selector fixture', ownerId: userId, @@ -100,7 +101,7 @@ describe('Copilot selectors backed by another domain', () => { }) }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) await db.$client.end() }) diff --git a/apps/sim/lib/table/rows/row-writes.integration.ts b/apps/sim/lib/table/rows/row-writes.integration.ts index e5c3a5de422..17d4b4d5922 100644 --- a/apps/sim/lib/table/rows/row-writes.integration.ts +++ b/apps/sim/lib/table/rows/row-writes.integration.ts @@ -122,8 +122,12 @@ describe('table row writes against real PostgreSQL', () => { beforeAll(async () => { await control`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at) VALUES (${userId}, 'Row write fixture', ${`${userId}@example.test`}, true, now(), now())` - await control`INSERT INTO workspace (id, name, owner_id, billed_account_user_id) + await control.begin(async (tx) => { + await tx`INSERT INTO workspace (id, name, owner_id, billed_account_user_id) VALUES (${workspaceId}, 'Row write fixtures', ${userId}, ${userId})` + await tx`INSERT INTO project (id, name, owner_id) VALUES (${workspaceId}, 'Fixture project', ${userId})` + await tx`INSERT INTO project_workspace (project_id, workspace_id) VALUES (${workspaceId}, ${workspaceId})` + }) }) beforeEach(() => { @@ -131,7 +135,10 @@ describe('table row writes against real PostgreSQL', () => { }) afterAll(async () => { - await control`DELETE FROM workspace WHERE id = ${workspaceId}` + await control.begin(async (tx) => { + await tx`DELETE FROM workspace WHERE id = ${workspaceId}` + await tx`DELETE FROM project WHERE id = ${workspaceId}` + }) await control`DELETE FROM "user" WHERE id = ${userId}` await control.end() }) diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts index f4baa51c973..0d016f80571 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts @@ -5,6 +5,7 @@ import { tmpdir } from 'node:os' import path from 'node:path' import { db, dbFor } from '@sim/db' import { organization, user, workspace, workspaceFiles } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNull, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -44,7 +45,7 @@ describe('workspace file names in PostgreSQL', () => { afterAll(async () => { vi.restoreAllMocks() for (const ids of fixtures) { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts index e88fdc4cab6..98a075a91e4 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts @@ -13,6 +13,7 @@ import { workspaceFiles, workspaceFileVersion, } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, asc, eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -68,7 +69,7 @@ describe('workspace file version history in PostgreSQL', () => { afterAll(async () => { for (const ids of fixtures) { - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/lib/webhooks/trigger-block-deployment.integration.ts b/apps/sim/lib/webhooks/trigger-block-deployment.integration.ts index 4b94dd6a7ca..b6fb5d22d49 100644 --- a/apps/sim/lib/webhooks/trigger-block-deployment.integration.ts +++ b/apps/sim/lib/webhooks/trigger-block-deployment.integration.ts @@ -5,6 +5,7 @@ */ import { db } from '@sim/db' import { user, webhook, workflow, workflowDeploymentVersion, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -35,9 +36,12 @@ beforeAll(async () => { createdAt: now, updatedAt: now, }) - await db - .insert(workspace) - .values({ id: workspaceId, name: 'Trigger Block', ownerId: owner, billedAccountUserId: owner }) + await insertWorkspaceFixture(db, { + id: workspaceId, + name: 'Trigger Block', + ownerId: owner, + billedAccountUserId: owner, + }) await db.insert(workflow).values( [deployedWorkflow, undeployedWorkflow].map((id) => ({ id, @@ -100,7 +104,7 @@ beforeAll(async () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, owner)) }) diff --git a/apps/sim/lib/workflows/application/compare-workflow-versions.integration.ts b/apps/sim/lib/workflows/application/compare-workflow-versions.integration.ts index 76e957180f4..bb057a080fd 100644 --- a/apps/sim/lib/workflows/application/compare-workflow-versions.integration.ts +++ b/apps/sim/lib/workflows/application/compare-workflow-versions.integration.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { user, workflow, workflowDeploymentVersion, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createWorkspaceApiKeyPrincipal } from '@sim/testing/factories/principal.factory' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' @@ -152,7 +153,8 @@ beforeAll(async () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values( + await insertWorkspaceFixture( + db, [workspaceId, otherWorkspaceId].map((id) => ({ id, name: 'Comparison fixture', @@ -188,7 +190,7 @@ beforeAll(async () => { }) afterAll(async () => { - await db.delete(workspace).where(inArray(workspace.id, [workspaceId, otherWorkspaceId])) + await deleteWorkspaceFixture(db, inArray(workspace.id, [workspaceId, otherWorkspaceId])) await db.delete(user).where(eq(user.id, ownerId)) await db.$client.end() }) diff --git a/apps/sim/lib/workflows/application/read-workflow-version.integration.ts b/apps/sim/lib/workflows/application/read-workflow-version.integration.ts index 7d03faa7bf1..327065fa4a1 100644 --- a/apps/sim/lib/workflows/application/read-workflow-version.integration.ts +++ b/apps/sim/lib/workflows/application/read-workflow-version.integration.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { permissions, user, workflow, workflowDeploymentVersion, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createSessionPrincipal, createWorkspaceApiKeyPrincipal, @@ -81,7 +82,7 @@ beforeAll(async () => { updatedAt: now, })) ) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Version read fixture', ownerId, @@ -111,7 +112,7 @@ beforeAll(async () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(inArray(user.id, [ownerId, outsiderId])) await db.$client.end() }) diff --git a/apps/sim/lib/workflows/custom-blocks/workspace-rows.integration.ts b/apps/sim/lib/workflows/custom-blocks/workspace-rows.integration.ts index a40c76c63f2..d47490f35b1 100644 --- a/apps/sim/lib/workflows/custom-blocks/workspace-rows.integration.ts +++ b/apps/sim/lib/workflows/custom-blocks/workspace-rows.integration.ts @@ -1,6 +1,7 @@ /** The custom-block rows an execution overlays, read against real PostgreSQL. */ import { db } from '@sim/db' import { customBlock, organization, user, workflow, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { billingSubscriptionMock, billingSubscriptionMockFns, @@ -38,7 +39,7 @@ beforeAll(async () => { { id: organizations.withBlocks, name: 'With Blocks', slug: organizations.withBlocks }, { id: organizations.withoutBlocks, name: 'Without Blocks', slug: organizations.withoutBlocks }, ]) - await db.insert(workspace).values([ + await insertWorkspaceFixture(db, [ { id: workspaces.withBlocks, name: 'With Blocks', @@ -90,7 +91,7 @@ beforeEach(() => { }) afterAll(async () => { - await db.delete(workspace).where(inArray(workspace.id, Object.values(workspaces))) + await deleteWorkspaceFixture(db, inArray(workspace.id, Object.values(workspaces))) await db.delete(organization).where(inArray(organization.id, Object.values(organizations))) await db.delete(user).where(eq(user.id, owner)) }) diff --git a/apps/sim/lib/workflows/executor/pause-persistence.integration.ts b/apps/sim/lib/workflows/executor/pause-persistence.integration.ts index 1d5c8eeafd9..be02edfa899 100644 --- a/apps/sim/lib/workflows/executor/pause-persistence.integration.ts +++ b/apps/sim/lib/workflows/executor/pause-persistence.integration.ts @@ -11,6 +11,7 @@ import { workflowExecutionSnapshots, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createDeferred } from '@sim/testing' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' @@ -129,7 +130,7 @@ beforeAll(async () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: ids.workspace, name: 'Pause Publish', ownerId: ids.owner, @@ -153,7 +154,7 @@ afterAll(async () => { await db .delete(workflowExecutionSnapshots) .where(eq(workflowExecutionSnapshots.workflowId, ids.workflow)) - await db.delete(workspace).where(eq(workspace.id, ids.workspace)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspace)) await db.delete(user).where(eq(user.id, ids.owner)) }) diff --git a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts index 1b30b3d4a3f..1eba3d90448 100644 --- a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts +++ b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts @@ -8,8 +8,6 @@ import { knowledgeBase, outboxEvent, permissions, - project, - projectWorkspace, user, userTableDefinitions, workflow, @@ -21,13 +19,13 @@ import { workspaceOperationReceipt, workspaceSandbox, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' import { withWorkspaceInvocationScope } from '@/lib/core/application/workspace-invocation-scope' import { processOutboxEventById } from '@/lib/core/outbox/service' import { createScopedCliTransport } from '@/lib/mothership/agent-cli/scoped-transport' -import { createProjectForWorkspace } from '@/lib/projects/membership' import { workflowDeploymentOutboxHandlers } from '@/lib/workflows/deployment-outbox' import { performCreateWorkflowTransition } from '@/lib/workflows/orchestration/workflow-lifecycle' import { duplicateWorkflow } from '@/lib/workflows/persistence/duplicate' @@ -133,21 +131,13 @@ describe('authorized fork and sync against PostgreSQL', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: sourceWorkspaceId, name: 'Fork source fixture', ownerId: userId, billedAccountUserId: userId, allowPersonalApiKeys: true, }) - await db.transaction((tx) => - createProjectForWorkspace(tx, { - workspaceId: sourceWorkspaceId, - name: 'Fork source fixture', - ownerId: userId, - organizationId: null, - }) - ) await db.insert(permissions).values({ id: generateId(), userId, @@ -180,21 +170,10 @@ describe('authorized fork and sync against PostgreSQL', () => { }) }) afterAll(async () => { - const owned = await db - .select({ id: project.id }) - .from(project) - .where(eq(project.ownerId, userId)) - if (owned.length) { - await db.delete(projectWorkspace).where( - inArray( - projectWorkspace.projectId, - owned.map((row) => row.id) - ) - ) - await db.delete(project).where(eq(project.ownerId, userId)) - } - for (const id of createdWorkspaceIds) await db.delete(workspace).where(eq(workspace.id, id)) - await db.delete(workspace).where(eq(workspace.id, sourceWorkspaceId)) + await deleteWorkspaceFixture( + db, + inArray(workspace.id, [sourceWorkspaceId, ...createdWorkspaceIds]) + ) await db.delete(user).where(eq(user.id, userId)) await db.$client.end() }) @@ -954,7 +933,8 @@ describe('authorized fork and sync against PostgreSQL', () => { const childWorkspaceId = generateId() const sourceWorkspaceId = generateId() createdWorkspaceIds.push(sourceWorkspaceId, childWorkspaceId) - await db.insert(workspace).values( + await insertWorkspaceFixture( + db, [sourceWorkspaceId, childWorkspaceId].map((id) => ({ id, name: 'Knowledge copy fixture', diff --git a/apps/sim/lib/workspaces/__integration__/http-cli.integration.ts b/apps/sim/lib/workspaces/__integration__/http-cli.integration.ts index 0a76531d69a..d7573e74d70 100644 --- a/apps/sim/lib/workspaces/__integration__/http-cli.integration.ts +++ b/apps/sim/lib/workspaces/__integration__/http-cli.integration.ts @@ -14,6 +14,7 @@ import { workspace, workspaceOperationReceipt, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { NextRequest } from 'next/server' @@ -111,7 +112,7 @@ describe('v2 and CLI workflow protocol against PostgreSQL', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'HTTP fixture', ownerId: userId, @@ -125,7 +126,7 @@ describe('v2 and CLI workflow protocol against PostgreSQL', () => { entityId: workspaceId, permissionType: 'admin', }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: foreignWorkspaceId, name: 'Inaccessible log fixture', ownerId: userId, @@ -237,9 +238,9 @@ describe('v2 and CLI workflow protocol against PostgreSQL', () => { server.close((error) => (error ? reject(error) : resolveClose())) server.closeAllConnections() }) - for (const id of childWorkspaceIds) await db.delete(workspace).where(eq(workspace.id, id)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) - await db.delete(workspace).where(eq(workspace.id, foreignWorkspaceId)) + for (const id of childWorkspaceIds) await deleteWorkspaceFixture(db, eq(workspace.id, id)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, foreignWorkspaceId)) await db .delete(workflowExecutionSnapshots) .where(eq(workflowExecutionSnapshots.id, logSnapshotId)) diff --git a/apps/sim/lib/workspaces/__integration__/mapped-import.integration.ts b/apps/sim/lib/workspaces/__integration__/mapped-import.integration.ts index b583499ae48..f3b45b96e9b 100644 --- a/apps/sim/lib/workspaces/__integration__/mapped-import.integration.ts +++ b/apps/sim/lib/workspaces/__integration__/mapped-import.integration.ts @@ -11,6 +11,7 @@ import { workspaceOperationReceipt, workspaceSandbox, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -103,7 +104,7 @@ describe('authorized mapped imports against PostgreSQL', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Mapped import fixture', ownerId: userId, @@ -127,7 +128,7 @@ describe('authorized mapped imports against PostgreSQL', () => { }) }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) await db.$client.end() }) diff --git a/apps/sim/lib/workspaces/__integration__/pagination.integration.ts b/apps/sim/lib/workspaces/__integration__/pagination.integration.ts index ef15cfe20b0..ef6d9548476 100644 --- a/apps/sim/lib/workspaces/__integration__/pagination.integration.ts +++ b/apps/sim/lib/workspaces/__integration__/pagination.integration.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { permissions, user, workspace, workspaceOperationReceipt } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -34,7 +35,8 @@ describe('workspace pagination against PostgreSQL', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values( + await insertWorkspaceFixture( + db, [workspaceId, otherWorkspaceId].map((id) => ({ id, name: 'Pagination parent fixture', @@ -52,7 +54,8 @@ describe('workspace pagination against PostgreSQL', () => { permissionType: 'admin' as const, })) ) - await db.insert(workspace).values( + await insertWorkspaceFixture( + db, childIds.map((id, index) => ({ id, name: `Pagination child ${index}`, @@ -89,9 +92,10 @@ describe('workspace pagination against PostgreSQL', () => { }) afterAll(async () => { - await db - .delete(workspace) - .where(inArray(workspace.id, [...childIds, workspaceId, otherWorkspaceId])) + await deleteWorkspaceFixture( + db, + inArray(workspace.id, [...childIds, workspaceId, otherWorkspaceId]) + ) await db.delete(user).where(eq(user.id, userId)) await db.$client.end() }) diff --git a/apps/sim/lib/workspaces/__integration__/receipts.integration.ts b/apps/sim/lib/workspaces/__integration__/receipts.integration.ts index 055c9dc7985..34340223bf7 100644 --- a/apps/sim/lib/workspaces/__integration__/receipts.integration.ts +++ b/apps/sim/lib/workspaces/__integration__/receipts.integration.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { user, workflow, workspace, workspaceOperationReceipt } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -25,7 +26,7 @@ describe('workspace receipts against PostgreSQL', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Workflow sync fixture', ownerId: userId, @@ -33,7 +34,7 @@ describe('workspace receipts against PostgreSQL', () => { }) }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) await db.$client.end() }) diff --git a/apps/sim/scripts/test-scim-e2e.ts b/apps/sim/scripts/test-scim-e2e.ts index 4ec8e28d00b..6ed9d360e7a 100644 --- a/apps/sim/scripts/test-scim-e2e.ts +++ b/apps/sim/scripts/test-scim-e2e.ts @@ -172,6 +172,10 @@ async function seedFixture(): Promise { values (${generateId()}, ${fixture.ownerId}, ${fixture.orgId}, 'owner')` await tx`insert into workspace (id, name, owner_id, billed_account_user_id, organization_id) values (${fixture.workspaceId}, 'SCIM E2E Workspace', ${fixture.ownerId}, ${fixture.ownerId}, ${fixture.orgId})` + await tx`insert into project (id, name, owner_id, organization_id) + values (${fixture.workspaceId}, 'SCIM E2E Project', ${fixture.ownerId}, ${fixture.orgId})` + await tx`insert into project_workspace (project_id, workspace_id) + values (${fixture.workspaceId}, ${fixture.workspaceId})` await tx`insert into permissions (id, user_id, entity_type, entity_id, permission_type) values (${generateId()}, ${fixture.ownerId}, 'workspace', ${fixture.workspaceId}, 'admin')` await tx`insert into subscription (id, plan, reference_id, status, seats, metadata, period_start, period_end) @@ -222,6 +226,7 @@ async function cleanup() { await tx`delete from rate_limit_bucket where key in ( select 'route:scim:connection:' || id from scim_connection where organization_id = ${fixture.orgId} )` + await tx`delete from project where organization_id = ${fixture.orgId}` await tx`delete from organization where id = ${fixture.orgId}` if (userIds.length) await tx`delete from "user" where id in ${tx(userIds)}` }) diff --git a/apps/sim/scripts/test-workflow-version-compare-e2e.ts b/apps/sim/scripts/test-workflow-version-compare-e2e.ts index 15b0b8f59c0..6c15bd053ce 100644 --- a/apps/sim/scripts/test-workflow-version-compare-e2e.ts +++ b/apps/sim/scripts/test-workflow-version-compare-e2e.ts @@ -222,6 +222,8 @@ async function seed() { for (const id of [workspaceId, foreignWorkspaceId]) { await tx`insert into workspace (id, name, owner_id, billed_account_user_id) values (${id}, 'Version comparison fixture', ${ownerId}, ${ownerId})` + await tx`insert into project (id, name, owner_id) values (${id}, 'Version comparison project', ${ownerId})` + await tx`insert into project_workspace (project_id, workspace_id) values (${id}, ${id})` } await tx`insert into permissions (id, user_id, entity_type, entity_id, permission_type) values (${generateId()}, ${ownerId}, 'workspace', ${workspaceId}, 'admin')` @@ -564,7 +566,10 @@ try { : 'remove disposable fixtures', async () => { if (!keepFixtures) { - await sql`delete from workspace where id in (${workspaceId}, ${foreignWorkspaceId})` + await sql.begin(async (tx) => { + await tx`delete from workspace where id in (${workspaceId}, ${foreignWorkspaceId})` + await tx`delete from project where id in (${workspaceId}, ${foreignWorkspaceId})` + }) await sql`delete from "user" where id in (${ownerId}, ${outsiderId})` } if (directory) await rm(directory, { recursive: true, force: true }) diff --git a/packages/db/maintenance/search-retirement.integration.ts b/packages/db/maintenance/search-retirement.integration.ts index 0ab85b505ef..025c1f4540a 100644 --- a/packages/db/maintenance/search-retirement.integration.ts +++ b/packages/db/maintenance/search-retirement.integration.ts @@ -103,8 +103,12 @@ describe('operator-driven Search retirement in PostgreSQL', () => { writer = postgres(fixtureUrl.toString(), { ...options, prepare: true }) await sql`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at) VALUES ('reader', 'Synthetic reader', 'reader@example.test', true, now(), now())` - await sql`INSERT INTO workspace (id, name, owner_id, billed_account_user_id) - VALUES ('workspace', 'Synthetic workspace', 'reader', 'reader')` + await sql.begin(async (tx) => { + await tx`INSERT INTO workspace (id, name, owner_id, billed_account_user_id) + VALUES ('workspace', 'Synthetic workspace', 'reader', 'reader')` + await tx`INSERT INTO project (id, name, owner_id) VALUES ('project', 'Synthetic project', 'reader')` + await tx`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('project', 'workspace')` + }) await sql`CREATE TABLE search_embedding_cleanup_progress ( id integer PRIMARY KEY, knowledge_base_id text, phase text, after_id text)` await sql`INSERT INTO search_embedding_cleanup_progress VALUES (1, 'search', 'embeddings', '')` diff --git a/packages/db/testing/workspace-fixtures.ts b/packages/db/testing/workspace-fixtures.ts new file mode 100644 index 00000000000..b23af0b0968 --- /dev/null +++ b/packages/db/testing/workspace-fixtures.ts @@ -0,0 +1,68 @@ +import type { db } from '@sim/db' +import { project, projectWorkspace, workspace } from '@sim/db/schema' +import { generateId } from '@sim/utils/id' +import { and, eq, inArray, notExists, type SQL } from 'drizzle-orm' +import type { PgInsertValue } from 'drizzle-orm/pg-core' + +type FixtureDatabase = typeof db +type FixtureTransaction = Parameters[0]>[0] +type WorkspaceRow = PgInsertValue + +/** Seeds real environments with the Project membership required by the migrated schema. */ +export async function insertWorkspaceFixture( + database: FixtureDatabase | FixtureTransaction, + values: WorkspaceRow | WorkspaceRow[] +) { + return database.transaction(async (tx) => { + const rows = await tx + .insert(workspace) + .values(Array.isArray(values) ? values : [values]) + .returning() + for (const row of rows) { + const [parent] = row.forkedFromWorkspaceId + ? await tx + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, row.forkedFromWorkspaceId)) + : [] + const projectId = parent?.projectId ?? generateId() + if (!parent) + await tx.insert(project).values({ + id: projectId, + name: 'Fixture project', + ownerId: row.ownerId, + organizationId: row.organizationId, + archivedAt: row.archivedAt, + }) + await tx.insert(projectWorkspace).values({ projectId, workspaceId: row.id }) + } + return rows + }) +} + +/** Removes fixture environments and their now-empty Projects in the same transaction. */ +export async function deleteWorkspaceFixture( + database: FixtureDatabase | FixtureTransaction, + condition: SQL | undefined +) { + await database.transaction(async (tx) => { + const rows = await tx + .select({ projectId: projectWorkspace.projectId }) + .from(workspace) + .innerJoin(projectWorkspace, eq(projectWorkspace.workspaceId, workspace.id)) + .where(condition) + await tx.delete(workspace).where(condition) + if (rows.length) + await tx.delete(project).where( + and( + inArray( + project.id, + rows.map((row) => row.projectId) + ), + notExists( + tx.select().from(projectWorkspace).where(eq(projectWorkspace.projectId, project.id)) + ) + ) + ) + }) +} diff --git a/scripts/test-integration.ts b/scripts/test-integration.ts index 1a5e4eee27b..1f7a70f81e6 100644 --- a/scripts/test-integration.ts +++ b/scripts/test-integration.ts @@ -11,6 +11,7 @@ import { generateId } from '@sim/utils/id' * Creates and removes its own Postgres and Redis containers, provisions the schema the way CI does, * and runs `vitest run --mode integration` in `packages/db` and `apps/sim` with `TEST_DATABASE_URL` * and `TEST_REDIS_URL`; it never reads an application DSN. + * Set INTEGRATION_DB_PROVISION=migrate to exercise the versioned SQL migration path. * Set KNOWLEDGE_SCALE_TEST=true for the opt-in scale suite; its JSON report is saved in tmpdir. * Optional positional `apps/sim` Vitest filename filters limit a diagnostic run (and skip * `packages/db`); omit them for full validation. @@ -20,6 +21,9 @@ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') const container = `sim-integration-test-${generateId()}` const redisContainer = `${container}-redis` const database = 'sim_test' +const provision = process.env.INTEGRATION_DB_PROVISION ?? 'push' +if (provision !== 'push' && provision !== 'migrate') + throw new Error('INTEGRATION_DB_PROVISION must be push or migrate') const scale = process.env.KNOWLEDGE_SCALE_TEST === 'true' const searchPerformance = process.env.KNOWLEDGE_SEARCH_PERFORMANCE_TEST === 'true' const testFilters = process.argv.slice(2) @@ -158,12 +162,12 @@ try { TEST_REDIS_URL: `redis://${redisEndpoint}`, ...(scale ? { KNOWLEDGE_SCALE_REPORT_FILE: scaleReportFile } : {}), } - run('bun', ['run', 'db:push'], { cwd: path.join(root, 'packages/db'), env: environment }) + run('bun', ['run', `db:${provision}`], { cwd: path.join(root, 'packages/db'), env: environment }) run( - 'bunx', + 'bun', [ - 'vitest', 'run', + 'test', '--mode', 'integration', ...(scale ? ['lib/knowledge/__integration__/scale.integration.ts'] : testFilters), @@ -174,7 +178,7 @@ try { } ) if (!scale && testFilters.length === 0) { - run('bunx', ['vitest', 'run', '--mode', 'integration'], { + run('bun', ['run', 'test', '--mode', 'integration'], { cwd: path.join(root, 'packages/db'), env: environment, }) From c1c8e5da95f2d9572240866483c27a02f22a4190 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 16:34:24 -0700 Subject: [PATCH 009/113] refactor(projects): prepare compatible writers for the SQL backfill --- .../lib/billing/organizations/membership.ts | 2 +- apps/sim/lib/core/config/env.ts | 1 - apps/sim/lib/projects/README.md | 6 +- .../__integration__/foundation.integration.ts | 124 ++--- apps/sim/lib/projects/account-deletion.ts | 9 +- apps/sim/lib/projects/membership.ts | 22 +- apps/sim/lib/projects/rollout.server.ts | 11 +- apps/sim/lib/workspaces/create.ts | 34 +- packages/db/package.json | 4 - packages/db/project-backfill.ts | 471 ------------------ packages/db/scripts/backfill-projects.ts | 205 -------- .../scripts/project-backfill.integration.ts | 427 ---------------- scripts/test-integration.ts | 12 +- 13 files changed, 103 insertions(+), 1225 deletions(-) delete mode 100644 packages/db/project-backfill.ts delete mode 100644 packages/db/scripts/backfill-projects.ts delete mode 100644 packages/db/scripts/project-backfill.integration.ts diff --git a/apps/sim/lib/billing/organizations/membership.ts b/apps/sim/lib/billing/organizations/membership.ts index ad406b11e1e..220b0dba82d 100644 --- a/apps/sim/lib/billing/organizations/membership.ts +++ b/apps/sim/lib/billing/organizations/membership.ts @@ -557,7 +557,7 @@ async function reassignOwnedOrganizationResourcesTx({ const ownerId = ownerMembership?.userId if (!ownerId || ownerId === userId) return 0 - await lockProjectBackfillWrites(tx) + await lockProjectBackfillWrites(tx, workspaceIds) const ownedProjects = await tx .select({ id: project.id }) .from(project) diff --git a/apps/sim/lib/core/config/env.ts b/apps/sim/lib/core/config/env.ts index 2fa5809732f..9904b0143e3 100644 --- a/apps/sim/lib/core/config/env.ts +++ b/apps/sim/lib/core/config/env.ts @@ -669,7 +669,6 @@ export const env = createEnv({ // SSO Configuration (for script-based registration) SSO_ENABLED: z.boolean().optional(), // Enable SSO functionality - PROJECT_WRITES_ENABLED: z.boolean().optional(), // Activate new Project assignments after old writers drain PROJECT_API_ENABLED: z.boolean().optional(), // Expose Projects after backfill and contract enforcement SCIM_ENABLED: z.boolean().optional(), // Enable SCIM directory provisioning USAGE_MONITORING_ENABLED: z.boolean().optional(), // Enable organization usage monitoring on self-hosted (bypasses hosted requirements) diff --git a/apps/sim/lib/projects/README.md b/apps/sim/lib/projects/README.md index fb81f7dbb26..88c3a330a9f 100644 --- a/apps/sim/lib/projects/README.md +++ b/apps/sim/lib/projects/README.md @@ -2,14 +2,14 @@ A Project groups environments. An environment is an existing `workspace` record; there is no separate environment table. Every newly created Project starts with an environment. -Project APIs return HTTP 503 until the deployment enables `PROJECT_API_ENABLED`. Automatic assignment on legacy workspace creation requires `PROJECT_WRITES_ENABLED`; both server controls default off. The API requires writers to be enabled. Existing assigned Projects always retain their lifecycle protections, including fork inheritance and disconnect behavior, even if activation is disabled. +Project APIs return HTTP 503 until the deployment enables `PROJECT_API_ENABLED`. Workspace creation always assigns a Project atomically. The API control defaults off and does not disable assignment. Existing assigned Projects always retain their lifecycle protections, including fork inheritance and disconnect behavior, even if activation is disabled. ## Choose the creation flow | Caller | Flow | Result | | --- | --- | --- | | New Project onboarding | `POST /api/projects` | Creates a Project and its first environment together, with independently supplied names. | -| Existing workspace creation UI or caller | `POST /api/workspaces` | Creates a workspace and, when Project writers are enabled, automatically creates its Project, preserving the existing workspace response. | +| Existing workspace creation UI or caller | `POST /api/workspaces` | Creates a workspace and automatically creates its Project, preserving the existing workspace response. | | Create another environment by forking | Existing workspace fork operation | Inherits the source workspace's Project; unassigned legacy families remain unassigned until backfilled. | Both POST endpoints are internal, session-authenticated APIs. `POST /api/projects` is not a public `/api/v2` endpoint and does not accept API-key principals. This foundation does not remove or deprecate existing workspace creation endpoints. @@ -69,7 +69,7 @@ Existing callers can continue to use `createWorkspaceContract` and `POST /api/wo `skipDefaultWorkflow` remains optional and defaults to `false`. The route uses the session's active organization and existing workspace creation policy to resolve ownership and billing. It does not take the explicit Project scope or independent Project name used by `POST /api/projects`. -When Project writers are enabled, the workspace and its Project are created atomically. The generated Project name is `Support workspace - Project`; long names are bounded to 100 characters while retaining the suffix. The response remains `{ "workspace": ... }` with HTTP 200, without a new Project response wrapper. Call `GET /api/projects/by-workspace/[workspaceId]` when an existing workspace caller needs its authorized Project details. +The workspace and its Project are created atomically. The generated Project name is `Support workspace - Project`; long names are bounded to 100 characters while retaining the suffix. The response remains `{ "workspace": ... }` with HTTP 200, without a new Project response wrapper. Call `GET /api/projects/by-workspace/[workspaceId]` when an existing workspace caller needs its authorized Project details. ## Server implementation diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 839f2190d5e..2512f49cb4d 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -55,7 +55,6 @@ import { createFork } from '@/ee/workspace-forking/lib/create-fork' import { unlinkForkEdge } from '@/ee/workspace-forking/lib/lineage/unlink' beforeEach(() => { - vi.stubEnv('PROJECT_WRITES_ENABLED', 'true') vi.stubEnv('PROJECT_API_ENABLED', 'true') }) @@ -194,9 +193,8 @@ afterAll(async () => { describe('Project foundation at the database and application boundary', () => { check( - 'disabled rollout preserves legacy creation and fork/disconnect without Project rows', + 'workspace creation and fork/disconnect assign Projects while APIs remain disabled', async () => { - vi.stubEnv('PROJECT_WRITES_ENABLED', 'false') vi.stubEnv('PROJECT_API_ENABLED', 'false') const f = await fixture(false, 1) const source = await db.transaction((tx) => @@ -214,7 +212,7 @@ describe('Project foundation at the database and application boundary', () => { environments.push(source.id) expect( await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, source.id)) - ).toEqual([]) + ).toHaveLength(1) const parent = await getWorkspaceWithOwner(source.id) if (!parent) throw new Error('Missing source fixture') const fork = await createFork({ @@ -229,11 +227,11 @@ describe('Project foundation at the database and application boundary', () => { .select() .from(projectWorkspace) .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) - ).toEqual([]) + ).toHaveLength(1) await unlinkForkEdge({ parentWorkspaceId: source.id, childWorkspaceId: fork.workspace.id }) const [child] = await db.select().from(workspace).where(eq(workspace.id, fork.workspace.id)) expect(child.forkedFromWorkspaceId).toBeNull() - expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(1) + expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(3) } ) @@ -242,45 +240,42 @@ describe('Project foundation at the database and application boundary', () => { async () => { const f = await fixture(false, 1) vi.stubEnv('PROJECT_API_ENABLED', 'false') - for (const writes of ['false', 'true']) { - vi.stubEnv('PROJECT_WRITES_ENABLED', writes) - const input = { projectId: f.projectId } - const calls = [ - () => - createProject.execute({ - principal: f.owner, - input: { - organizationId: null, - name: 'Blocked', - initialEnvironment: { name: 'Production' }, - }, - request, - }), - () => getProject.execute({ principal: f.owner, input, request }), - () => - getWorkspaceProject.execute({ - principal: f.owner, - input: { workspaceId: f.ids[0] }, - request, - }), - () => listProjects.execute({ principal: f.owner, input: { limit: 10 }, request }), - () => - renameProject.execute({ - principal: f.owner, - input: { ...input, name: 'Blocked' }, - request, - }), - () => archiveProject.execute({ principal: f.owner, input, request }), - () => getProjectIssueAccess.execute({ principal: f.owner, input, request }), - ] - for (const call of calls) await expect(call()).rejects.toMatchObject({ statusCode: 503 }) - expect( - await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId)) - ).toHaveLength(1) - const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) - expect(record.name).toBe('Environment 0 - Project') - expect(record.archivedAt).toBeNull() - } + const input = { projectId: f.projectId } + const calls = [ + () => + createProject.execute({ + principal: f.owner, + input: { + organizationId: null, + name: 'Blocked', + initialEnvironment: { name: 'Production' }, + }, + request, + }), + () => getProject.execute({ principal: f.owner, input, request }), + () => + getWorkspaceProject.execute({ + principal: f.owner, + input: { workspaceId: f.ids[0] }, + request, + }), + () => listProjects.execute({ principal: f.owner, input: { limit: 10 }, request }), + () => + renameProject.execute({ + principal: f.owner, + input: { ...input, name: 'Blocked' }, + request, + }), + () => archiveProject.execute({ principal: f.owner, input, request }), + () => getProjectIssueAccess.execute({ principal: f.owner, input, request }), + ] + for (const call of calls) await expect(call()).rejects.toMatchObject({ statusCode: 503 }) + expect( + await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId)) + ).toHaveLength(1) + const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(record.name).toBe('Environment 0 - Project') + expect(record.archivedAt).toBeNull() } ) @@ -288,7 +283,6 @@ describe('Project foundation at the database and application boundary', () => { 'disabling activation preserves assigned fork membership and lifecycle protections', async () => { const f = await fixture(false, 1) - vi.stubEnv('PROJECT_WRITES_ENABLED', 'false') vi.stubEnv('PROJECT_API_ENABLED', 'false') const parent = await getWorkspaceWithOwner(f.ids[0]) if (!parent) throw new Error('Missing source fixture') @@ -316,7 +310,7 @@ describe('Project foundation at the database and application boundary', () => { } ) - check('writer activation assigns new workspaces while Project APIs remain disabled', async () => { + check('new workspaces receive Projects while Project APIs remain disabled', async () => { vi.stubEnv('PROJECT_API_ENABLED', 'false') const f = await fixture(false, 1) const created = await db.transaction((tx) => @@ -335,18 +329,6 @@ describe('Project foundation at the database and application boundary', () => { expect( await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, created.id)) ).toHaveLength(1) - vi.stubEnv('PROJECT_WRITES_ENABLED', 'false') - vi.stubEnv('PROJECT_API_ENABLED', 'true') - await expect( - createProject.execute({ - principal: f.owner, - input: { organizationId: null, name: 'Invalid', initialEnvironment: { name: 'First' } }, - request, - }) - ).rejects.toThrow('PROJECT_API_ENABLED requires PROJECT_WRITES_ENABLED') - expect(await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId))).toHaveLength( - 2 - ) }) check('legacy fork and disconnect refuse a partially assigned subtree', async () => { @@ -354,7 +336,6 @@ describe('Project foundation at the database and application boundary', () => { await db .delete(projectWorkspace) .where(inArray(projectWorkspace.workspaceId, f.ids.slice(0, 2))) - vi.stubEnv('PROJECT_WRITES_ENABLED', 'false') vi.stubEnv('PROJECT_API_ENABLED', 'false') const parent = await getWorkspaceWithOwner(f.ids[1]) if (!parent) throw new Error('Missing source fixture') @@ -395,20 +376,23 @@ describe('Project foundation at the database and application boundary', () => { }) try { await Promise.race([read.promise, writer]) - await expect( - db.transaction(async (tx) => { - await tx.execute( - sql`LOCK TABLE workspace, project, project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT` - ) - }) - ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '55P03') + await db.transaction(async (tx) => { + const [lock] = await tx.execute<{ acquired: boolean }>(sql` + SELECT pg_try_advisory_xact_lock(hashtextextended(${`project-backfill:${f.ids[0]}`}, 0)) AS acquired + `) + expect(lock.acquired).toBe(false) + const [unrelated] = await tx.execute<{ acquired: boolean }>(sql` + SELECT pg_try_advisory_xact_lock(hashtextextended('project-backfill:unrelated', 0)) AS acquired + `) + expect(unrelated.acquired).toBe(true) + }) } finally { release.resolve() await writer } await db.transaction(async (tx) => { await tx.execute( - sql`LOCK TABLE workspace, project, project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT` + sql`SELECT pg_advisory_xact_lock(hashtextextended(${`project-backfill:${f.ids[0]}`}, 0))` ) await createProjectForWorkspace(tx, { workspaceId: f.ids[0], @@ -434,7 +418,7 @@ describe('Project foundation at the database and application boundary', () => { const release = createDeferred() const backfill = db.transaction(async (tx) => { await tx.execute( - sql`LOCK TABLE workspace, project, project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT` + sql`SELECT pg_advisory_xact_lock(hashtextextended(${`project-backfill:${f.ids[0]}`}, 0))` ) locked.resolve() await release.promise @@ -459,7 +443,7 @@ describe('Project foundation at the database and application boundary', () => { try { for (let attempt = 0; attempt < 100; attempt++) { const rows = await db.execute<{ waiting: boolean }>(sql`SELECT EXISTS ( - SELECT 1 FROM pg_locks WHERE relation = 'workspace'::regclass AND mode = 'RowExclusiveLock' AND NOT granted + SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND mode = 'ShareLock' AND NOT granted ) AS waiting`) if (rows[0]?.waiting) { blocked = true diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts index 795c999dd57..98e14251a87 100644 --- a/apps/sim/lib/projects/account-deletion.ts +++ b/apps/sim/lib/projects/account-deletion.ts @@ -10,7 +10,14 @@ export async function prepareProjectsForAccountDeletion( userId: string, doomedWorkspaceIds: string[] ): Promise { - await lockProjectBackfillWrites(tx) + const ownedEnvironments = await tx + .select({ id: workspace.id }) + .from(workspace) + .where(eq(workspace.ownerId, userId)) + await lockProjectBackfillWrites(tx, [ + ...doomedWorkspaceIds, + ...ownedEnvironments.map((row) => row.id), + ]) const records = await tx .select({ id: project.id }) .from(project) diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index 54c45c6e36c..5efe1592ecb 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -5,11 +5,21 @@ import { and, asc, eq, inArray, isNull, ne, notInArray, sql } from 'drizzle-orm' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbOrTx, DbTransaction } from '@/lib/db/types' -/** Prevents backfill from assigning membership between an absence read and the ensuing write. */ -export async function lockProjectBackfillWrites(tx: DbTransaction): Promise { +/** Shared per-environment gate keeps membership absence reads stable during SQL backfill. */ +export async function lockProjectBackfillWrites( + tx: DbTransaction, + workspaceIds: string[] +): Promise { + if (!workspaceIds.length) return await tx.execute(sql`SET LOCAL lock_timeout = '5s'`) try { - await tx.execute(sql`LOCK TABLE workspace IN ROW EXCLUSIVE MODE`) + await tx.execute(sql` + SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:' || id, 0)) + FROM (SELECT DISTINCT unnest(ARRAY[${sql.join( + workspaceIds.map((id) => sql`${id}`), + sql`, ` + )}]::text[]) AS id ORDER BY id) ids + `) } catch (error) { if (getPostgresErrorCode(error) === '55P03') throw new OrchestrationError('conflict', 'Project backfill is running; retry the operation') @@ -59,9 +69,9 @@ export async function createProjectForWorkspace( return id } -/** Returns null only for a legacy workspace awaiting the operator-run backfill. */ +/** Returns null only for a legacy workspace awaiting the SQL backfill. */ export async function lockWorkspaceProject(tx: DbTransaction, workspaceId: string) { - await lockProjectBackfillWrites(tx) + await lockProjectBackfillWrites(tx, [workspaceId]) const [membership] = await tx .select() .from(projectWorkspace) @@ -218,7 +228,7 @@ export async function transferWorkspaceProjects( ownerId?: string ): Promise { if (!workspaceIds.length) return - await lockProjectBackfillWrites(tx) + await lockProjectBackfillWrites(tx, workspaceIds) const owners = await tx .selectDistinct({ id: projectWorkspace.projectId }) .from(projectWorkspace) diff --git a/apps/sim/lib/projects/rollout.server.ts b/apps/sim/lib/projects/rollout.server.ts index 3ffe5d81e8c..1f6e24962e4 100644 --- a/apps/sim/lib/projects/rollout.server.ts +++ b/apps/sim/lib/projects/rollout.server.ts @@ -1,15 +1,6 @@ import { envBoolean, getEnv } from '@/lib/core/config/env' import { HttpError } from '@/lib/core/utils/http-error' -/** Deployment-wide controls; existing Project lifecycle maintenance never depends on these. */ -export function getProjectRollout() { - const writesEnabled = envBoolean(getEnv('PROJECT_WRITES_ENABLED')) ?? false - const apiEnabled = envBoolean(getEnv('PROJECT_API_ENABLED')) ?? false - if (apiEnabled && !writesEnabled) - throw new Error('PROJECT_API_ENABLED requires PROJECT_WRITES_ENABLED') - return { writesEnabled, apiEnabled } -} - class ProjectUnavailableError extends HttpError { readonly statusCode = 503 constructor() { @@ -18,5 +9,5 @@ class ProjectUnavailableError extends HttpError { } export function requireProjectApiEnabled(): void { - if (!getProjectRollout().apiEnabled) throw new ProjectUnavailableError() + if (!(envBoolean(getEnv('PROJECT_API_ENABLED')) ?? false)) throw new ProjectUnavailableError() } diff --git a/apps/sim/lib/workspaces/create.ts b/apps/sim/lib/workspaces/create.ts index d81847a487f..4b1549bd0ae 100644 --- a/apps/sim/lib/workspaces/create.ts +++ b/apps/sim/lib/workspaces/create.ts @@ -6,7 +6,7 @@ import { generateId } from '@sim/utils/id' import { PlatformEvents } from '@/lib/core/telemetry' import type { DbTransaction } from '@/lib/db/types' import { createProjectForWorkspace } from '@/lib/projects/membership' -import { getProjectRollout, requireProjectApiEnabled } from '@/lib/projects/rollout.server' +import { requireProjectApiEnabled } from '@/lib/projects/rollout.server' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' @@ -94,7 +94,6 @@ async function createWorkspaceRecordsInTransaction( tx: DbTransaction, { projectName, - assignProject, userId, observedOrganizationId, name, @@ -103,8 +102,8 @@ async function createWorkspaceRecordsInTransaction( workspaceMode, billedAccountUserId, governingPermissionGroupOrganizationId, - }: TransactionalCreateWorkspaceParams & { projectName?: string; assignProject: boolean } -): Promise<{ projectId: string | null; workspace: CreatedWorkspace }> { + }: TransactionalCreateWorkspaceParams & { projectName?: string } +): Promise<{ projectId: string; workspace: CreatedWorkspace }> { const workspaceId = generateId() const workflowId = generateId() const now = new Date() @@ -133,15 +132,13 @@ async function createWorkspaceRecordsInTransaction( updatedAt: now, }) - const projectId = assignProject - ? await createProjectForWorkspace(tx, { - projectName, - workspaceId, - name, - organizationId: organizationId ?? null, - ownerId: userId, - }) - : null + const projectId = await createProjectForWorkspace(tx, { + projectName, + workspaceId, + name, + organizationId: organizationId ?? null, + ownerId: userId, + }) const permissionRows = [ { @@ -213,9 +210,7 @@ export async function createWorkspaceWithProjectInTransaction( params: TransactionalCreateWorkspaceParams & { projectName?: string } ): Promise<{ projectId: string; workspace: CreatedWorkspace }> { requireProjectApiEnabled() - const created = await createWorkspaceRecordsInTransaction(tx, { ...params, assignProject: true }) - if (!created.projectId) throw new Error('Project creation did not assign a Project') - return { ...created, projectId: created.projectId } + return createWorkspaceRecordsInTransaction(tx, params) } /** Preserves the workspace-only result for existing creation callers. */ @@ -223,12 +218,7 @@ export async function createWorkspaceInTransaction( tx: DbTransaction, params: TransactionalCreateWorkspaceParams ): Promise { - return ( - await createWorkspaceRecordsInTransaction(tx, { - ...params, - assignProject: getProjectRollout().writesEnabled, - }) - ).workspace + return (await createWorkspaceRecordsInTransaction(tx, params)).workspace } /** Creates a workspace through the canonical lock-and-insert transaction. */ diff --git a/packages/db/package.json b/packages/db/package.json index 9ff079b5d8c..31cf5145650 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -28,10 +28,6 @@ "./sso-primary-provider": { "types": "./sso-primary-provider.ts", "default": "./sso-primary-provider.ts" - }, - "./project-backfill": { - "types": "./project-backfill.ts", - "default": "./project-backfill.ts" } }, "scripts": { diff --git a/packages/db/project-backfill.ts b/packages/db/project-backfill.ts deleted file mode 100644 index 181cfe17246..00000000000 --- a/packages/db/project-backfill.ts +++ /dev/null @@ -1,471 +0,0 @@ -import { createHash } from 'node:crypto' -import { createLogger } from '@sim/logger' -import { classifyDatabaseFailure, getPostgresErrorCode } from '@sim/utils/errors' -import { sleep } from '@sim/utils/helpers' -import { generateId } from '@sim/utils/id' -import { backoffWithJitter } from '@sim/utils/retry' -import { compareStrings, truncate } from '@sim/utils/string' -import postgres, { type Sql, type TransactionSql } from 'postgres' - -type BackfillSql = Sql | TransactionSql - -const logger = createLogger('ProjectBackfill', { enabled: true, logLevel: 'INFO' }) -const MAX_FAMILY = 10_000 -const MAX_WORKSPACES = 250_000 -const RUN_LOCK = 'sim-project-backfill-v1' - -interface LegacyWorkspace { - id: string - name: string - organization_id: string | null - owner_id: string - archived_at: string | null - forked_from_workspace_id: string | null -} - -interface FamilyPlan { - rootId: string - projectId: string - workspaceIds: string[] - fingerprint: string - name: string -} - -interface Conflict { - workspaceId: string - projectIds: string[] - workspaceIds: string[] - reason: string -} - -export interface ProjectBackfillReport { - version: 1 - databaseId: string - mode: 'dry-run' | 'apply' | 'verify' - startedAt: string - finishedAt?: string - completed: boolean - ready: boolean - stopped?: string - errorCode?: string - scanned: number - families: number - createdProjects: number - assignedWorkspaces: number - unchangedFamilies: number - retries: number - plan: FamilyPlan[] - conflicts: Conflict[] -} - -interface BackfillOptions { - mode: ProjectBackfillReport['mode'] - databaseId: string - plan?: ProjectBackfillReport - maxFamilySize?: number - seconds?: number - shouldStop?: () => boolean - onProgress?: (report: ProjectBackfillReport) => Promise -} - -function fingerprint(family: LegacyWorkspace[]): string { - return createHash('sha256').update(JSON.stringify(family)).digest('hex') -} - -function conflict( - rootId: string, - reason: string, - ids: string[], - projects: string[] = [] -): Conflict { - return { workspaceId: rootId, reason, workspaceIds: ids, projectIds: projects } -} - -/** Only reads and transactions known to have rolled back may be retried. */ -async function withDatabaseRetry( - operation: () => Promise, - write: boolean, - report: ProjectBackfillReport, - stopped: () => boolean -): Promise { - for (let attempt = 1; ; attempt++) { - try { - return await operation() - } catch (error) { - const code = getPostgresErrorCode(error) - const category = classifyDatabaseFailure(error) - const retryable = write - ? ['40001', '40P01', '55P03'].includes(code ?? '') - : category === 'connection' || ['53300', '57P03'].includes(code ?? '') - if (!retryable || attempt >= 3 || stopped()) throw error - report.retries++ - logger.warn('Retrying Project backfill database operation', { code, write, attempt }) - await sleep(backoffWithJitter(attempt, null, { baseMs: 250, maxMs: 2000 })) - } - } -} - -async function configureTransaction(tx: BackfillSql) { - await tx`SET LOCAL lock_timeout = '2s'` - await tx`SET LOCAL statement_timeout = '30s'` - await tx`SET LOCAL idle_in_transaction_session_timeout = '30s'` - await tx`SET LOCAL transaction_timeout = '30s'` -} - -async function discoverFamily(tx: BackfillSql, rootId: string, maximum: number) { - const family = await tx` - WITH RECURSIVE family AS ( - SELECT id, name, organization_id, owner_id, archived_at::text AS archived_at, forked_from_workspace_id FROM workspace WHERE id = ${rootId} - UNION - SELECT w.id, w.name, w.organization_id, w.owner_id, w.archived_at::text, w.forked_from_workspace_id - FROM workspace w JOIN family f ON w.forked_from_workspace_id = f.id - ) SELECT * FROM family LIMIT ${maximum + 1} - ` - return family.sort((a, b) => compareStrings(a.id, b.id)) -} - -async function inspectFamily(tx: BackfillSql, rootId: string, maximum: number) { - const family = await discoverFamily(tx, rootId, maximum) - const root = family.find((row) => row.id === rootId) - const ids = family.map((row) => row.id) - if (!root || root.forked_from_workspace_id !== null) - return { conflict: conflict(rootId, 'Root missing or now attached to another family', ids) } - if (family.length > maximum) - return { conflict: conflict(rootId, 'Family exceeds batch limit', ids) } - if (family.some((row) => row.organization_id !== root.organization_id)) - return { conflict: conflict(rootId, 'Family spans organizations', ids) } - const memberships = await tx<{ project_id: string }[]>` - SELECT DISTINCT project_id FROM project_workspace WHERE workspace_id = ANY(${ids}::text[]) ORDER BY project_id - ` - const projectIds = memberships.map((row) => row.project_id) - if (projectIds.length > 1) - return { conflict: conflict(rootId, 'Family spans multiple Projects', ids, projectIds) } - const existingId = projectIds[0] - if (existingId) { - const [existing] = await tx<{ organization_id: string | null; archived_at: Date | null }[]>` - SELECT organization_id, archived_at FROM project WHERE id = ${existingId} - ` - if (!existing || existing.organization_id !== root.organization_id) - return { conflict: conflict(rootId, 'Project organization differs', ids, projectIds) } - const outside = - await tx`SELECT workspace_id FROM project_workspace WHERE project_id = ${existingId} AND NOT (workspace_id = ANY(${ids}::text[])) LIMIT 1` - if (outside.length) - return { conflict: conflict(rootId, 'Project includes another lineage', ids, projectIds) } - if (Boolean(existing.archived_at) !== family.every((row) => row.archived_at !== null)) - return { conflict: conflict(rootId, 'Project archive state differs', ids, projectIds) } - } - const [{ missing }] = await tx<{ missing: number }[]>` - SELECT count(*)::int AS missing FROM workspace w WHERE w.id = ANY(${ids}::text[]) - AND NOT EXISTS (SELECT 1 FROM project_workspace pw WHERE pw.workspace_id = w.id) - ` - return { root, family, ids, existingId, missing } -} - -async function applyFamily(tx: BackfillSql, planned: FamilyPlan, maximum: number) { - // NOWAIT prevents waiting behind application writers with an opposite row/advisory lock order. - await tx`LOCK TABLE workspace, project, project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT` - const state = await inspectFamily(tx, planned.rootId, maximum) - if (state.conflict) return { conflict: state.conflict, created: 0, assigned: 0 } - if (fingerprint(state.family) !== planned.fingerprint) - return { - conflict: conflict( - planned.rootId, - 'Family changed since dry-run; produce a new plan', - state.ids - ), - created: 0, - assigned: 0, - } - if (state.existingId && state.existingId !== planned.projectId) - return { - conflict: conflict(planned.rootId, 'Project assignment changed since dry-run', state.ids, [ - state.existingId, - planned.projectId, - ]), - created: 0, - assigned: 0, - } - if (!state.existingId) { - await tx`INSERT INTO project (id, name, organization_id, owner_id, archived_at) - SELECT ${planned.projectId}, ${planned.name}, ${state.root.organization_id}, ${state.root.owner_id}, - CASE WHEN count(archived_at) = count(*) THEN max(archived_at) ELSE NULL END - FROM workspace WHERE id = ANY(${state.ids}::text[])` - } - const inserted = await tx`INSERT INTO project_workspace (project_id, workspace_id) - SELECT ${planned.projectId}, id FROM workspace WHERE id = ANY(${state.ids}::text[]) - AND NOT EXISTS (SELECT 1 FROM project_workspace pw WHERE pw.workspace_id = workspace.id)` - return { created: state.existingId ? 0 : 1, assigned: inserted.count } -} - -async function inspectGlobalState(tx: BackfillSql) { - const rows = await tx<{ id: string; reason: string }[]>` - SELECT p.id, 'Empty Project' AS reason FROM project p - WHERE NOT EXISTS (SELECT 1 FROM project_workspace pw WHERE pw.project_id = p.id) - UNION ALL - SELECT pw.project_id, 'Orphan membership or organization mismatch' FROM project_workspace pw - LEFT JOIN project p ON p.id = pw.project_id LEFT JOIN workspace w ON w.id = pw.workspace_id - WHERE p.id IS NULL OR w.id IS NULL OR p.organization_id IS DISTINCT FROM w.organization_id - LIMIT 1001 - ` - if (rows.length > 1000) throw new Error('Global anomaly report limit exceeded; repair and rerun') - return rows.map((row) => conflict('', row.reason, [], [row.id])) -} - -/** Validate the operator artifact before it can select any write target. */ -export function readProjectBackfillPlan(value: unknown, databaseId: string): ProjectBackfillReport { - if (!value || typeof value !== 'object') throw new Error('Invalid dry-run artifact') - const plan = value as Partial - if ( - plan.version !== 1 || - plan.mode !== 'dry-run' || - !plan.completed || - plan.databaseId !== databaseId || - !Array.isArray(plan.plan) || - !Array.isArray(plan.conflicts) || - plan.conflicts.length - ) - throw new Error('Apply requires a completed, conflict-free dry-run for this database') - let total = 0 - const roots = new Set() - const projects = new Set() - const members = new Set() - for (const family of plan.plan) { - if ( - !family || - typeof family.rootId !== 'string' || - !family.rootId || - typeof family.projectId !== 'string' || - !family.projectId || - typeof family.fingerprint !== 'string' || - !/^[a-f0-9]{64}$/.test(family.fingerprint) || - typeof family.name !== 'string' || - family.name.trim().length < 1 || - family.name.length > 100 || - !Array.isArray(family.workspaceIds) || - !family.workspaceIds.length || - family.workspaceIds.length > MAX_FAMILY - ) - throw new Error('Malformed family in dry-run artifact') - if (roots.has(family.rootId) || projects.has(family.projectId)) - throw new Error('Duplicate planned root or Project') - roots.add(family.rootId) - projects.add(family.projectId) - for (const id of family.workspaceIds) { - if (typeof id !== 'string' || !id || members.has(id)) - throw new Error('Invalid or repeated planned environment') - members.add(id) - } - if (!family.workspaceIds.includes(family.rootId)) - throw new Error('Root missing from dry-run family') - total += family.workspaceIds.length - if (total > MAX_WORKSPACES) throw new Error('Dry-run artifact exceeds workspace limit') - } - return plan as ProjectBackfillReport -} - -/** Bounded operator backfill. Reports are checkpointed before writes and after each commit. */ -export async function backfillProjects( - sql: Sql, - options: BackfillOptions -): Promise { - const maximum = options.maxFamilySize ?? MAX_FAMILY - const seconds = options.seconds ?? 600 - if ( - !Number.isInteger(maximum) || - maximum < 1 || - maximum > MAX_FAMILY || - !Number.isInteger(seconds) || - seconds < 1 || - seconds > 3600 - ) - throw new Error('Invalid family or runtime limit') - const source = - options.mode === 'apply' ? readProjectBackfillPlan(options.plan, options.databaseId) : null - const report: ProjectBackfillReport = { - version: 1, - databaseId: options.databaseId, - mode: options.mode, - startedAt: new Date().toISOString(), - completed: false, - ready: false, - scanned: 0, - families: 0, - createdProjects: 0, - assignedWorkspaces: 0, - unchangedFamilies: 0, - retries: 0, - plan: [], - conflicts: [], - } - const deadline = Date.now() + seconds * 1000 - const stopped = () => Boolean(options.shouldStop?.()) || Date.now() >= deadline - const checkpoint = async () => { - await options.onProgress?.(report) - } - let lockLost = false - if (sql.options.host.length !== 1 || sql.options.port.length !== 1) - throw new Error('Backfill requires a single direct primary endpoint') - const lockClient = postgres({ - host: sql.options.host[0], - port: sql.options.port[0], - database: sql.options.database, - user: sql.options.user, - password: sql.options.pass ?? undefined, - ssl: sql.options.ssl, - connection: sql.options.connection, - onnotice: sql.options.onnotice, - max: 1, - idle_timeout: 0, - max_lifetime: null, - connect_timeout: 10, - onclose: () => { - lockLost = true - }, - }) - const connection = await lockClient.reserve() - let locked = false - let backendPid = 0 - try { - // Session settings also bound discovery and the first lock attempt. - await connection`SET statement_timeout = '30s'` - await connection`SET lock_timeout = '2s'` - const [identity] = await connection<{ pid: number; locked: boolean }[]>` - SELECT pg_backend_pid() AS pid, pg_try_advisory_lock(hashtextextended(${RUN_LOCK}, 0)) AS locked - ` - locked = identity.locked - backendPid = identity.pid - if (!locked) throw new Error('Another Project backfill run holds the maintenance lock') - const assertLock = async () => { - if (lockLost) throw new Error('Project backfill maintenance lock was lost') - const [state] = await connection<{ held: boolean }[]>` - SELECT pg_backend_pid() = ${backendPid} AND EXISTS ( - SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND pid = pg_backend_pid() - AND classid = ((hashtextextended(${RUN_LOCK}, 0) >> 32) & 4294967295)::oid - AND objid = (hashtextextended(${RUN_LOCK}, 0) & 4294967295)::oid AND objsubid = 1 AND granted - ) AS held - ` - if (!state.held) throw new Error('Project backfill maintenance lock was lost') - } - const transaction = async ( - work: (tx: BackfillSql) => Promise, - write = false - ): Promise => - withDatabaseRetry( - async () => { - await assertLock() - const result = await sql.begin(async (tx) => { - await configureTransaction(tx) - const value = await work(tx) - await assertLock() - return value - }) - return result as T - }, - write, - report, - stopped - ) - await checkpoint() - if (source) { - for (const family of source.plan) { - if (stopped()) break - const outcome = await transaction((tx) => applyFamily(tx, family, maximum), true) - report.scanned += family.workspaceIds.length - report.families++ - if (outcome.conflict) report.conflicts.push(outcome.conflict) - report.createdProjects += outcome.created - report.assignedWorkspaces += outcome.assigned - if (!outcome.conflict && outcome.assigned === 0) report.unchangedFamilies++ - await checkpoint() - } - report.completed = report.families === source.plan.length - } else { - let afterId = '' - for (;;) { - if (stopped()) break - const page = await transaction( - (tx) => - tx< - { id: string }[] - >`SELECT id FROM workspace WHERE id > ${afterId} ORDER BY id LIMIT 100` - ) - if (!page.length) { - report.completed = true - break - } - for (const candidate of page) { - if (stopped()) break - if (++report.scanned > MAX_WORKSPACES) throw new Error('Workspace scan limit exceeded') - const discovery = await transaction(async (tx) => { - const [root] = await tx<{ id: string; parent: string | null; cycle: boolean }[]>` - WITH RECURSIVE ancestors AS ( - SELECT id, forked_from_workspace_id AS parent, ARRAY[id] AS path, false AS cycle, 0 AS depth FROM workspace WHERE id = ${candidate.id} - UNION ALL - SELECT w.id, w.forked_from_workspace_id, a.path || w.id, w.id = ANY(a.path), a.depth + 1 - FROM workspace w JOIN ancestors a ON w.id = a.parent WHERE NOT a.cycle AND a.depth < 1000 - ) SELECT id, parent, cycle FROM ancestors ORDER BY depth DESC LIMIT 1 - ` - if (!root || root.cycle || root.parent !== null) - return { - conflict: conflict( - candidate.id, - 'Cycle, missing parent, or lineage depth exceeds 1000', - [candidate.id] - ), - } - if (root.id !== candidate.id) return null - return inspectFamily(tx, root.id, maximum) - }) - if (discovery) { - if (discovery.conflict) report.conflicts.push(discovery.conflict) - else { - report.families++ - const suffix = ' - Project' - report.plan.push({ - rootId: candidate.id, - projectId: discovery.existingId ?? generateId(), - workspaceIds: discovery.ids, - fingerprint: fingerprint(discovery.family), - name: `${truncate(discovery.root.name.trim() || 'Untitled', 100 - suffix.length, '')}${suffix}`, - }) - report.createdProjects += discovery.existingId ? 0 : 1 - report.assignedWorkspaces += discovery.missing - if (discovery.missing === 0) report.unchangedFamilies++ - } - } - afterId = candidate.id - } - await checkpoint() - } - if (report.completed) report.conflicts.push(...(await transaction(inspectGlobalState))) - } - report.ready = - options.mode === 'verify' && - report.completed && - !report.conflicts.length && - report.assignedWorkspaces === 0 - if (!report.completed) - report.stopped = 'Interrupted or runtime budget exhausted; rerun from the same plan' - report.finishedAt = new Date().toISOString() - await checkpoint() - return report - } catch (error) { - report.completed = false - report.ready = false - report.errorCode = getPostgresErrorCode(error) - report.stopped = report.errorCode - ? `Database operation failed (${report.errorCode}); reconcile before retrying writes` - : 'Run failed; inspect operator logs and last checkpoint' - report.finishedAt = new Date().toISOString() - await checkpoint() - throw error - } finally { - try { - if (locked && !lockLost) - await connection`SELECT pg_advisory_unlock(hashtextextended(${RUN_LOCK}, 0))` - } finally { - connection.release() - await lockClient.end({ timeout: 2 }) - } - } -} diff --git a/packages/db/scripts/backfill-projects.ts b/packages/db/scripts/backfill-projects.ts deleted file mode 100644 index 3dc9775b5e2..00000000000 --- a/packages/db/scripts/backfill-projects.ts +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env bun - -import { createHash } from 'node:crypto' -import { readFile, rename, stat, writeFile } from 'node:fs/promises' -import { resolve } from 'node:path' -import { parseArgs } from 'node:util' -import { backfillProjects, readProjectBackfillPlan } from '@sim/db/project-backfill' -import { createLogger } from '@sim/logger' -import { getErrorMessage, getPostgresErrorCode } from '@sim/utils/errors' -import postgres from 'postgres' - -const logger = createLogger('ProjectBackfillCommand', { enabled: true, logLevel: 'INFO' }) -const HELP = `Operator-only Project backfill. Deployment only creates the additive tables. - -bun --no-env-file packages/db/scripts/backfill-projects.ts [options] - -Commands: - identity Print the non-secret database fingerprint; no database writes. - dry-run Discover and validate families; write the plan/report. - apply Apply --from-file PLAN from a completed, conflict-free dry-run. - verify Independently reconcile all environments and Projects; nonzero unless ready. - -Required environment: PROJECT_BACKFILL_DATABASE_URL (direct primary connection). -Reports: PROJECT_BACKFILL_REPORT_PATH (required except identity). -Apply requires --writers-drained --project-writers-enabled --release-revision REVISION -and --database-id FINGERPRINT. These are operator assertions, not fleet verification. -Deploy first with PROJECT_WRITES_ENABLED=false and PROJECT_API_ENABLED=false. -Drain old code, enable Project writers on every compatible instance, then backfill. -Keep the Project API off until verification and later contract enforcement. -Once Project data exists, never roll back to code that predates Project support. -Options: --from-file PATH, --seconds 600 (1–3600), --max-family-size 1000 (1–10000). -Run with compatible writers deployed. Pause lineage/ownership writes during apply. -Short NOWAIT table locks exclude concurrent changes during each family transaction. -Interrupted/failed apply: inspect the report, then rerun the SAME dry-run artifact. -Connection loss during writes is never automatically retried; verify the committed state. -No run enables Project consumers or installs the deferred contract constraints.` - -async function main() { - const { positionals, values } = parseArgs({ - args: process.argv.slice(2), - allowPositionals: true, - options: { - help: { type: 'boolean' }, - 'from-file': { type: 'string' }, - 'writers-drained': { type: 'boolean' }, - 'project-writers-enabled': { type: 'boolean' }, - 'release-revision': { type: 'string' }, - 'database-id': { type: 'string' }, - seconds: { type: 'string', default: '600' }, - 'max-family-size': { type: 'string', default: '1000' }, - }, - }) - if (values.help) { - process.stdout.write(`${HELP}\n`) - return - } - const mode = positionals[0] ?? 'dry-run' - if (positionals.length > 1 || !['identity', 'dry-run', 'apply', 'verify'].includes(mode)) - throw new Error('Unknown command; use --help') - const rawUrl = process.env.PROJECT_BACKFILL_DATABASE_URL - if (!rawUrl) - throw new Error('Set PROJECT_BACKFILL_DATABASE_URL explicitly; no application DSN fallback') - const url = new URL(rawUrl) - if ( - !['postgres:', 'postgresql:'].includes(url.protocol) || - !url.hostname || - !url.username || - url.pathname.length < 2 - ) - throw new Error('Expected a direct PostgreSQL database URL') - const databaseId = createHash('sha256') - .update( - JSON.stringify([ - url.hostname.toLowerCase(), - url.port || '5432', - decodeURIComponent(url.pathname), - ]) - ) - .digest('hex') - if (mode === 'identity') { - process.stdout.write(`${databaseId}\n`) - return - } - if (mode !== 'dry-run' && mode !== 'apply' && mode !== 'verify') - throw new Error('Invalid command') - const reportPath = process.env.PROJECT_BACKFILL_REPORT_PATH - if (!reportPath) throw new Error('Set PROJECT_BACKFILL_REPORT_PATH explicitly') - const releaseRevision = values['release-revision']?.trim() ?? '' - if ( - mode === 'apply' && - (!values['writers-drained'] || - !values['project-writers-enabled'] || - !releaseRevision || - values['database-id'] !== databaseId || - !values['from-file']) - ) - throw new Error( - 'Apply requires --writers-drained, --project-writers-enabled, --release-revision, matching --database-id, and --from-file' - ) - if ( - mode !== 'apply' && - (values['writers-drained'] || - values['project-writers-enabled'] || - values['release-revision'] || - values['from-file']) - ) - throw new Error('Apply-only options used with read-only mode') - const operatorAssertions = - mode === 'apply' - ? { - oldWritersDrained: true, - projectWritersEnabled: true, - releaseRevision, - } - : undefined - const inputPath = values['from-file'] - if (inputPath && resolve(inputPath) === resolve(reportPath)) - throw new Error('Keep the dry-run plan and apply report in separate files') - const plan = inputPath - ? await (async () => { - if ((await stat(inputPath)).size > 32 * 1024 * 1024) - throw new Error('Dry-run artifact exceeds 32 MiB') - return readProjectBackfillPlan(JSON.parse(await readFile(inputPath, 'utf8')), databaseId) - })() - : undefined - const sql = postgres(rawUrl, { - max: 1, - prepare: false, - connect_timeout: 10, - idle_timeout: 0, - max_lifetime: null, - connection: { - application_name: 'sim-project-backfill', - statement_timeout: 30_000, - lock_timeout: 2000, - }, - }) - let interrupted = false - const stop = () => { - interrupted = true - } - process.on('SIGINT', stop) - process.on('SIGTERM', stop) - try { - // Preflight the report destination before opening a transaction that can write. - const partialPath = `${reportPath}.${process.pid}.partial` - await writeFile( - partialPath, - JSON.stringify({ - databaseId, - mode, - operatorAssertions, - completed: false, - ready: false, - stopped: 'Starting', - }), - { mode: 0o600 } - ) - await rename(partialPath, reportPath) - const report = await backfillProjects(sql, { - mode, - databaseId, - plan, - seconds: Number(values.seconds), - maxFamilySize: Number(values['max-family-size']), - shouldStop: () => interrupted, - onProgress: async (progress) => { - await writeFile(partialPath, JSON.stringify({ ...progress, operatorAssertions }, null, 2), { - mode: 0o600, - }) - await rename(partialPath, reportPath) - logger.info('Project backfill progress', { - mode, - databaseId, - scanned: progress.scanned, - families: progress.families, - createdProjects: progress.createdProjects, - assignedWorkspaces: progress.assignedWorkspaces, - conflicts: progress.conflicts.length, - retries: progress.retries, - completed: progress.completed, - }) - }, - }) - if (!report.completed || report.conflicts.length || (mode === 'verify' && !report.ready)) - process.exitCode = 1 - } finally { - process.off('SIGINT', stop) - process.off('SIGTERM', stop) - await sql.end({ timeout: 5 }) - } -} - -try { - await main() -} catch (error) { - const code = getPostgresErrorCode(error) - // Database messages can contain connection details or row values; retain SQLSTATE, not the payload. - logger.error('Project backfill failed', { - error: code - ? `Database error ${code}; inspect the report and reconcile before retrying` - : getErrorMessage(error), - }) - process.exitCode = 1 -} diff --git a/packages/db/scripts/project-backfill.integration.ts b/packages/db/scripts/project-backfill.integration.ts deleted file mode 100644 index dc4a4c30e8c..00000000000 --- a/packages/db/scripts/project-backfill.integration.ts +++ /dev/null @@ -1,427 +0,0 @@ -import { spawn } from 'node:child_process' -import { readFile } from 'node:fs/promises' -import { backfillProjects, type ProjectBackfillReport } from '@sim/db/project-backfill' -import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' -import { generateId } from '@sim/utils/id' -import postgres, { type Sql } from 'postgres' -import { describe, expect, it } from 'vitest' - -/** Isolated pre-Project fixture; applies the actual additive migration, never a schema mock. */ -async function withDatabase(run: (sql: Sql, url: string) => Promise) { - const admin = postgres(readTestDatabaseUrl(), { max: 1 }) - const database = `project_backfill_test_${generateId().replaceAll('-', '')}` - const url = new URL(readTestDatabaseUrl()) - url.pathname = `/${database}` - await admin.unsafe(`CREATE DATABASE "${database}"`) - const sql = postgres(url.toString(), { max: 1, onnotice: () => undefined }) - try { - await sql.unsafe(` - CREATE TABLE "user" (id text PRIMARY KEY); - CREATE TABLE organization (id text PRIMARY KEY); - CREATE TABLE workspace ( - id text PRIMARY KEY, name text NOT NULL, owner_id text NOT NULL, - organization_id text, archived_at timestamp, forked_from_workspace_id text - ); - INSERT INTO "user" VALUES ('owner'), ('other-owner'); - INSERT INTO organization VALUES ('org-a'), ('org-b'); - `) - await sql.unsafe( - await readFile(new URL('../migrations/0393_project_foundation.sql', import.meta.url), 'utf8') - ) - await run(sql, url.toString()) - } finally { - await sql.end({ timeout: 2 }) - await admin.unsafe(`DROP DATABASE "${database}" WITH (FORCE)`) - await admin.end() - } -} - -async function seed(sql: Sql) { - await sql`INSERT INTO workspace (id, name, owner_id, organization_id, forked_from_workspace_id, archived_at) VALUES - ('a-root', 'Production', 'owner', 'org-a', NULL, NULL), - ('b-child', 'Staging', 'other-owner', 'org-a', 'a-root', NULL), - ('c-grandchild', 'Dev', 'owner', 'org-a', 'b-child', NULL), - ('d-detached', 'Staging', 'owner', NULL, NULL, NULL), - ('e-detached-child', 'Dev', 'owner', NULL, 'd-detached', NULL), - ('f-archived', 'Archive', 'owner', NULL, NULL, '2025-01-01'), - ('g-archived-child', 'Archive child', 'owner', NULL, 'f-archived', '2025-02-01')` -} - -const databaseId = 'local-test-database' -const dryRun = (sql: Sql) => backfillProjects(sql, { mode: 'dry-run', databaseId }) -const verify = (sql: Sql) => backfillProjects(sql, { mode: 'verify', databaseId }) -const apply = (sql: Sql, plan: ProjectBackfillReport) => - backfillProjects(sql, { mode: 'apply', databaseId, plan }) - -describe('Project backfill operator lifecycle', () => { - it('plans roots and detached families, retains archive timestamps, and replays fixed IDs', async () => { - await withDatabase(async (sql) => { - await seed(sql) - const plan = await dryRun(sql) - expect(plan.createdProjects).toBe(3) - expect(plan.assignedWorkspaces).toBe(7) - expect(await sql`SELECT * FROM project`).toHaveLength(0) - expect((await verify(sql)).ready).toBe(false) - const first = await apply(sql, plan) - expect(first.assignedWorkspaces).toBe(7) - expect((await verify(sql)).ready).toBe(true) - const rows = await sql`SELECT id, name, owner_id, archived_at FROM project ORDER BY name` - expect(rows.map((row) => row.name)).toEqual([ - 'Archive - Project', - 'Production - Project', - 'Staging - Project', - ]) - expect( - ( - await sql`SELECT archived_at::text AS value FROM project WHERE name = 'Archive - Project'` - )[0].value - ).toBe('2025-02-01 00:00:00') - expect(rows[1].owner_id).toBe('owner') - const repeat = await apply(sql, plan) - expect(repeat.createdProjects).toBe(0) - expect(repeat.unchangedFamilies).toBe(3) - expect(await sql`SELECT id, name, owner_id, archived_at FROM project ORDER BY name`).toEqual( - rows - ) - }) - }) - - it('completes partial assignments but blocks split Projects, cycles, missing parents and mixed organizations', async () => { - await withDatabase(async (sql) => { - await seed(sql) - await sql`INSERT INTO project (id, name, owner_id, organization_id) VALUES ('existing', 'Keep this name', 'owner', 'org-a')` - await sql`INSERT INTO project_workspace VALUES ('existing', 'a-root', now())` - const plan = await dryRun(sql) - await apply(sql, plan) - expect( - await sql`SELECT workspace_id FROM project_workspace WHERE project_id = 'existing'` - ).toHaveLength(3) - expect((await sql`SELECT name FROM project WHERE id = 'existing'`)[0].name).toBe( - 'Keep this name' - ) - await sql`INSERT INTO project (id, name, owner_id, organization_id) VALUES ('split', 'Split', 'owner', 'org-a'), ('empty', 'Empty', 'owner', NULL)` - await sql`UPDATE project_workspace SET project_id = 'split' WHERE workspace_id = 'b-child'` - await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) VALUES ('cycle-a', 'A', 'owner', 'cycle-b'), ('cycle-b', 'B', 'owner', 'cycle-a'), ('orphan', 'Orphan', 'owner', 'missing')` - await sql`UPDATE workspace SET organization_id = 'org-b' WHERE id = 'e-detached-child'` - const broken = await verify(sql) - expect(broken.ready).toBe(false) - expect(broken.conflicts.map((row) => row.reason)).toEqual( - expect.arrayContaining([ - 'Family spans multiple Projects', - 'Family spans organizations', - 'Empty Project', - 'Cycle, missing parent, or lineage depth exceeds 1000', - ]) - ) - expect(broken.conflicts.find((row) => row.workspaceId === 'a-root')?.projectIds).toEqual([ - 'existing', - 'split', - ]) - await expect(apply(sql, { ...broken, mode: 'dry-run' })).rejects.toThrow('conflict-free') - }) - }) - - it('rejects a new descendant added after dry-run without assigning a partial family', async () => { - await withDatabase(async (sql) => { - await seed(sql) - const plan = await dryRun(sql) - await sql`INSERT INTO workspace (id, name, owner_id, organization_id, forked_from_workspace_id) VALUES ('late-child', 'Late', 'owner', 'org-a', 'b-child')` - const result = await apply(sql, plan) - expect( - result.conflicts.some( - (row) => row.workspaceId === 'a-root' && row.reason.includes('changed since dry-run') - ) - ).toBe(true) - expect( - await sql`SELECT * FROM project_workspace WHERE workspace_id IN ('a-root', 'b-child', 'c-grandchild', 'late-child')` - ).toHaveLength(0) - const fresh = await dryRun(sql) - await apply(sql, fresh) - expect((await verify(sql)).ready).toBe(true) - }) - }) - - it('resumes after a committed-family interruption and retains progress when reporting fails', async () => { - await withDatabase(async (sql) => { - await seed(sql) - const plan = await dryRun(sql) - let stop = false - const partial = await backfillProjects(sql, { - mode: 'apply', - databaseId, - plan, - shouldStop: () => stop, - onProgress: async (report) => { - if (report.families === 1) stop = true - }, - }) - expect(partial.completed).toBe(false) - expect(partial.createdProjects).toBe(1) - const firstId = (await sql`SELECT id FROM project`)[0].id - await expect( - backfillProjects(sql, { - mode: 'apply', - databaseId, - plan, - onProgress: async (report) => { - if (report.createdProjects > 0) throw new Error('Report disk unavailable') - }, - }) - ).rejects.toThrow('Report disk unavailable') - expect(await sql`SELECT id FROM project`).toHaveLength(2) - await apply(sql, plan) - expect(await sql`SELECT id FROM project WHERE id = ${firstId}`).toHaveLength(1) - expect((await verify(sql)).ready).toBe(true) - }) - }) - - it('refuses another runner while a run owns the lock and stops on a lost database session', async () => { - await withDatabase(async (sql, url) => { - await seed(sql) - const other = postgres(url, { max: 1 }) - try { - let checked = false - await backfillProjects(sql, { - mode: 'dry-run', - databaseId, - onProgress: async () => { - if (checked) return - checked = true - await expect(dryRun(other)).rejects.toThrow('maintenance lock') - }, - }) - const plan = await dryRun(sql) - let terminated = false - let snapshot: ProjectBackfillReport | undefined - await expect( - backfillProjects(sql, { - mode: 'apply', - databaseId, - plan, - onProgress: async (report) => { - snapshot = structuredClone(report) - if (terminated || report.families !== 1) return - terminated = true - await other`SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND pid <> pg_backend_pid()` - }, - }) - ).rejects.toThrow() - expect(snapshot?.completed).toBe(false) - expect(snapshot?.createdProjects).toBe(1) - await apply(other, plan) - expect((await verify(other)).ready).toBe(true) - } finally { - await other.end({ timeout: 2 }) - } - }) - }) - - it('fails boundedly behind a live writer, then succeeds after the writer finishes', async () => { - await withDatabase(async (sql, url) => { - await seed(sql) - const plan = await dryRun(sql) - const writer = postgres(url, { max: 1 }) - try { - await writer.begin(async (tx) => { - await tx`UPDATE workspace SET name = name WHERE id = 'a-root'` - let last: ProjectBackfillReport | undefined - await expect( - backfillProjects(sql, { - mode: 'apply', - databaseId, - plan, - onProgress: async (report) => { - last = structuredClone(report) - }, - }) - ).rejects.toMatchObject({ code: '55P03' }) - expect(last?.retries).toBe(2) - expect(last?.createdProjects).toBe(0) - }) - await apply(sql, plan) - expect((await verify(sql)).ready).toBe(true) - } finally { - await writer.end() - } - }) - }) - - it('retries rolled-back serialization failures but stops on cancellation without partial writes', async () => { - await withDatabase(async (sql) => { - await seed(sql) - const plan = await dryRun(sql) - await sql.unsafe(` - CREATE SEQUENCE attempts; - CREATE FUNCTION fail_twice() RETURNS trigger LANGUAGE plpgsql AS $$ - BEGIN - IF nextval('attempts') <= 2 THEN RAISE EXCEPTION 'injected serialization failure' USING ERRCODE = '40001'; END IF; - RETURN NEW; - END $$; - CREATE TRIGGER fail_twice BEFORE INSERT ON project FOR EACH ROW EXECUTE FUNCTION fail_twice(); - `) - const result = await apply(sql, plan) - expect(result.retries).toBe(2) - expect(result.createdProjects).toBe(3) - expect((await verify(sql)).ready).toBe(true) - await sql`DELETE FROM project_workspace` - await sql`DELETE FROM project` - await sql.unsafe(` - DROP TRIGGER fail_twice ON project; - CREATE FUNCTION cancel_write() RETURNS trigger LANGUAGE plpgsql AS $$ - BEGIN RAISE EXCEPTION 'injected cancellation' USING ERRCODE = '57014'; END $$; - CREATE TRIGGER cancel_write BEFORE INSERT ON project FOR EACH ROW EXECUTE FUNCTION cancel_write(); - `) - let report: ProjectBackfillReport | undefined - await expect( - backfillProjects(sql, { - mode: 'apply', - databaseId, - plan, - onProgress: async (progress) => { - report = structuredClone(progress) - }, - }) - ).rejects.toMatchObject({ code: '57014' }) - expect(report?.retries).toBe(0) - expect(report?.errorCode).toBe('57014') - expect(await sql`SELECT * FROM project_workspace`).toHaveLength(0) - expect(await sql`SELECT * FROM project`).toHaveLength(0) - }) - }) - - it('fails readiness for empty Projects and archived-state mismatch without mutating them', async () => { - await withDatabase(async (sql) => { - await seed(sql) - await apply(sql, await dryRun(sql)) - await sql`UPDATE project SET archived_at = now() WHERE name = 'Production - Project'` - await sql`INSERT INTO project (id, name, owner_id) VALUES ('empty', 'Empty', 'owner')` - const result = await verify(sql) - expect(result.ready).toBe(false) - expect(result.conflicts.map((row) => row.reason)).toEqual( - expect.arrayContaining(['Empty Project', 'Project archive state differs']) - ) - expect(await sql`SELECT id FROM project WHERE id = 'empty'`).toHaveLength(1) - }) - }) -}) - -/** CLI cases exercise the normal connection contract against an isolated database. */ -describe('Project backfill CLI', () => { - it('runs dry-run/apply/verify with durable reports and refuses a mismatched target', async () => { - const { mkdtemp, rm } = await import('node:fs/promises') - const { tmpdir } = await import('node:os') - const { join } = await import('node:path') - await withDatabase(async (sql, url) => { - await seed(sql) - const directory = await mkdtemp(join(tmpdir(), 'project-backfill-test-')) - const planPath = join(directory, 'plan.json') - const reportPath = join(directory, 'report.json') - const run = async (args: string[], report = reportPath) => { - const child = spawn('bun', ['--no-env-file', 'scripts/backfill-projects.ts', ...args], { - env: { - ...process.env, - PROJECT_BACKFILL_DATABASE_URL: url, - PROJECT_BACKFILL_REPORT_PATH: report, - }, - stdio: ['ignore', 'pipe', 'pipe'], - }) - let stdout = '' - let stderr = '' - child.stdout.setEncoding('utf8').on('data', (chunk: string) => { - stdout += chunk - }) - child.stderr.setEncoding('utf8').on('data', (chunk: string) => { - stderr += chunk - }) - const code = await new Promise((resolve, reject) => { - child.on('error', reject) - child.on('close', resolve) - }) - expect(stdout + stderr).not.toContain(url) - return { stdout, stderr, code } - } - try { - const dry = await run(['dry-run'], planPath) - expect(dry.code, dry.stdout + dry.stderr).toBe(0) - const plan = JSON.parse(await readFile(planPath, 'utf8')) as ProjectBackfillReport - for (const missing of [ - '--project-writers-enabled', - '--release-revision', - '--writers-drained', - ]) { - const args = ['apply', '--from-file', planPath, '--database-id', plan.databaseId] - if (missing !== '--writers-drained') args.push('--writers-drained') - if (missing !== '--project-writers-enabled') args.push('--project-writers-enabled') - if (missing !== '--release-revision') - args.push('--release-revision', 'integration-compatible-release') - const refused = await run(args) - expect(refused.code).toBe(1) - expect(refused.stdout + refused.stderr).toContain('Apply requires') - expect(await sql`SELECT * FROM project`).toHaveLength(0) - } - - expect( - ( - await run([ - 'apply', - '--from-file', - planPath, - '--writers-drained', - '--project-writers-enabled', - '--release-revision', - 'integration-compatible-release', - '--database-id', - 'wrong', - ]) - ).code - ).toBe(1) - expect(await sql`SELECT * FROM project`).toHaveLength(0) - expect( - ( - await run([ - 'apply', - '--from-file', - planPath, - '--writers-drained', - '--project-writers-enabled', - '--release-revision', - 'integration-compatible-release', - '--database-id', - plan.databaseId, - ]) - ).code - ).toBe(0) - const applied = JSON.parse(await readFile(reportPath, 'utf8')) - expect(applied.operatorAssertions).toEqual({ - oldWritersDrained: true, - projectWritersEnabled: true, - releaseRevision: 'integration-compatible-release', - }) - expect((await run(['verify'])).code).toBe(0) - const report = JSON.parse(await readFile(reportPath, 'utf8')) as ProjectBackfillReport - expect(report.ready).toBe(true) - expect( - ( - await run( - [ - 'apply', - '--from-file', - planPath, - '--writers-drained', - '--project-writers-enabled', - '--release-revision', - 'integration-compatible-release', - '--database-id', - plan.databaseId, - ], - planPath - ) - ).code - ).toBe(1) - expect(JSON.parse(await readFile(planPath, 'utf8')).mode).toBe('dry-run') - } finally { - await rm(directory, { recursive: true, force: true }) - } - }) - }) -}) diff --git a/scripts/test-integration.ts b/scripts/test-integration.ts index 1a5e4eee27b..1f7a70f81e6 100644 --- a/scripts/test-integration.ts +++ b/scripts/test-integration.ts @@ -11,6 +11,7 @@ import { generateId } from '@sim/utils/id' * Creates and removes its own Postgres and Redis containers, provisions the schema the way CI does, * and runs `vitest run --mode integration` in `packages/db` and `apps/sim` with `TEST_DATABASE_URL` * and `TEST_REDIS_URL`; it never reads an application DSN. + * Set INTEGRATION_DB_PROVISION=migrate to exercise the versioned SQL migration path. * Set KNOWLEDGE_SCALE_TEST=true for the opt-in scale suite; its JSON report is saved in tmpdir. * Optional positional `apps/sim` Vitest filename filters limit a diagnostic run (and skip * `packages/db`); omit them for full validation. @@ -20,6 +21,9 @@ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') const container = `sim-integration-test-${generateId()}` const redisContainer = `${container}-redis` const database = 'sim_test' +const provision = process.env.INTEGRATION_DB_PROVISION ?? 'push' +if (provision !== 'push' && provision !== 'migrate') + throw new Error('INTEGRATION_DB_PROVISION must be push or migrate') const scale = process.env.KNOWLEDGE_SCALE_TEST === 'true' const searchPerformance = process.env.KNOWLEDGE_SEARCH_PERFORMANCE_TEST === 'true' const testFilters = process.argv.slice(2) @@ -158,12 +162,12 @@ try { TEST_REDIS_URL: `redis://${redisEndpoint}`, ...(scale ? { KNOWLEDGE_SCALE_REPORT_FILE: scaleReportFile } : {}), } - run('bun', ['run', 'db:push'], { cwd: path.join(root, 'packages/db'), env: environment }) + run('bun', ['run', `db:${provision}`], { cwd: path.join(root, 'packages/db'), env: environment }) run( - 'bunx', + 'bun', [ - 'vitest', 'run', + 'test', '--mode', 'integration', ...(scale ? ['lib/knowledge/__integration__/scale.integration.ts'] : testFilters), @@ -174,7 +178,7 @@ try { } ) if (!scale && testFilters.length === 0) { - run('bunx', ['vitest', 'run', '--mode', 'integration'], { + run('bun', ['run', 'test', '--mode', 'integration'], { cwd: path.join(root, 'packages/db'), env: environment, }) From 5e11d7d5bb6d44fd1b15816808b6bd0589d64ffc Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 16:50:40 -0700 Subject: [PATCH 010/113] refactor(projects): backfill through bounded SQL migration batches --- .github/scripts/check-project-rollout.py | 8 +- .github/workflows/migrations.yml | 4 +- .../__integration__/foundation.integration.ts | 12 +- .../0394_project_membership_enforcement.sql | 161 +++++++- .../scripts/project-contract.integration.ts | 379 ++++++++++++++++-- 5 files changed, 513 insertions(+), 51 deletions(-) diff --git a/.github/scripts/check-project-rollout.py b/.github/scripts/check-project-rollout.py index 6c6c410b3ae..25aefd4beb7 100644 --- a/.github/scripts/check-project-rollout.py +++ b/.github/scripts/check-project-rollout.py @@ -2,8 +2,8 @@ """Read-only, fail-closed ECS retirement check for the Project contract migration. The expected digest is the operator's release-scoped acknowledgment that Project -writers are enabled, relevant old worker jobs are drained, and backfill verification -passed. AWS checks below independently verify ECS retirement, not those assertions. +compatible writers are deployed and relevant old worker jobs are drained. +AWS checks below independently verify ECS retirement, not worker drainage. """ import argparse import json @@ -24,7 +24,7 @@ def aws(region, *args): def verify(environment, region, digest): if not re.fullmatch(r'sha256:[0-9a-f]{64}', digest): - raise RuntimeError('Set the environment-specific PROJECT_ENFORCEMENT_READY_IMAGE_DIGEST after reviewing rollout and backfill evidence') + raise RuntimeError('Set the environment-specific PROJECT_ENFORCEMENT_READY_IMAGE_DIGEST after reviewing compatible rollout and worker-drain evidence') pipeline = f'sim-{environment}-{region}-app-deployment' executions = aws(region, 'codepipeline', 'list-pipeline-executions', '--pipeline-name', pipeline).get('pipelineExecutionSummaries', []) if not executions or executions[0].get('status') != 'Succeeded': @@ -65,7 +65,7 @@ def verify(environment, region, digest): if not latest or latest[0].get('pipelineExecutionId') != execution_id or latest[0].get('status') != 'Succeeded': raise RuntimeError('Application deployment changed during preflight') print(json.dumps({'ecsRetired': True, 'expectedImageDigest': digest, 'pipelineExecutionId': execution_id, - 'operatorAcknowledgedWritersWorkersAndBackfill': True})) + 'operatorAcknowledgedCompatibleWritersAndWorkers': True})) if __name__ == '__main__': diff --git a/.github/workflows/migrations.yml b/.github/workflows/migrations.yml index 7d634da683b..b4a9459b12b 100644 --- a/.github/workflows/migrations.yml +++ b/.github/workflows/migrations.yml @@ -68,8 +68,8 @@ jobs: role-to-assume: ${{ inputs.environment == 'production' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }} aws-region: ${{ inputs.environment == 'production' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }} - # Set this release-scoped acknowledgment only after writers are enabled everywhere, - # relevant old Trigger.dev runs are drained, and the reviewed backfill verifies ready. + # Set this release-scoped acknowledgment only after compatible writers are deployed + # everywhere and relevant old Trigger.dev runs are drained. SQL performs the backfill. # The preflight independently checks ECS retirement; it does not inspect worker runs. - name: Require completed compatible rollout before Project enforcement if: steps.project-contract.outputs.required == 'true' diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index cd551d201f3..9afe3b1e003 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -202,7 +202,7 @@ afterAll(async () => { if (restoreEnforcement) { const client = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => undefined }) try { - await client.unsafe( + for (const statement of ( await readFile( new URL( '../../../../../packages/db/migrations/0394_project_membership_enforcement.sql', @@ -210,7 +210,9 @@ afterAll(async () => { ), 'utf8' ) - ) + ).split('--> statement-breakpoint')) { + await client.unsafe(statement) + } } finally { await client.end() } @@ -223,7 +225,7 @@ describe('Project foundation at the database and application boundary', () => { async () => { const client = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => undefined }) try { - await client.unsafe( + for (const statement of ( await readFile( new URL( '../../../../../packages/db/migrations/0394_project_membership_enforcement.sql', @@ -231,7 +233,9 @@ describe('Project foundation at the database and application boundary', () => { ), 'utf8' ) - ) + ).split('--> statement-breakpoint')) { + await client.unsafe(statement) + } const f = await fixture(true, 1) const organizationId = f.organizationId if (!organizationId) throw new Error('Missing organization fixture') diff --git a/packages/db/migrations/0394_project_membership_enforcement.sql b/packages/db/migrations/0394_project_membership_enforcement.sql index b5c669807cd..4e080c4c6b5 100644 --- a/packages/db/migrations/0394_project_membership_enforcement.sql +++ b/packages/db/migrations/0394_project_membership_enforcement.sql @@ -1,15 +1,144 @@ -BEGIN; +-- Each family commits separately; never retain locks across the full backfill. +-- The migration runner serializes runners with its session advisory lock. +COMMIT; +--> statement-breakpoint +SET statement_timeout = '15min'; +--> statement-breakpoint +CREATE TEMP TABLE IF NOT EXISTS project_backfill_roots (id text PRIMARY KEY) ON COMMIT PRESERVE ROWS; +TRUNCATE project_backfill_roots; +INSERT INTO project_backfill_roots SELECT id FROM workspace WHERE forked_from_workspace_id IS NULL; +--> statement-breakpoint +CREATE OR REPLACE PROCEDURE pg_temp.backfill_project_families() LANGUAGE plpgsql AS $$ +DECLARE + root_id text; + previous_root text; + environment_id text; + family_ids text[]; + current_ids text[]; + project_ids text[]; + target_project text; + root workspace%ROWTYPE; + existing project%ROWTYPE; + archive_time timestamp; + attempts integer := 0; + completed integer := 0; + assigned integer := 0; + inserted integer; + retry boolean; +BEGIN + LOOP + SELECT id INTO root_id FROM project_backfill_roots + WHERE previous_root IS NULL OR id > previous_root ORDER BY id LIMIT 1; + EXIT WHEN root_id IS NULL; + -- A pathological family cannot hold environment locks indefinitely (PostgreSQL 17+). + PERFORM set_config('transaction_timeout', '5s', true); + PERFORM set_config('lock_timeout', '1s', true); + retry := false; + BEGIN + WITH RECURSIVE family(id) AS ( + SELECT id FROM workspace WHERE id = root_id AND forked_from_workspace_id IS NULL + UNION + SELECT w.id FROM workspace w JOIN family f ON w.forked_from_workspace_id = f.id + ) SELECT array_agg(id ORDER BY id) INTO family_ids FROM (SELECT id FROM family LIMIT 1001) bounded; + IF cardinality(family_ids) > 1000 THEN + RAISE EXCEPTION 'Project backfill family exceeds 1000 environments' USING ERRCODE = '54000', DETAIL = root_id; + END IF; + IF family_ids IS NOT NULL THEN + -- Application writers take the shared form before reading membership, including absence. + FOREACH environment_id IN ARRAY family_ids LOOP + IF NOT pg_try_advisory_xact_lock(hashtextextended('project-backfill:' || environment_id, 0)) THEN + RAISE EXCEPTION 'Project backfill environment is busy' USING ERRCODE = '55P03'; + END IF; + END LOOP; + -- Do not wait while holding a partial lock set; unrelated environments remain writable. + PERFORM id FROM workspace WHERE id = ANY(family_ids) ORDER BY id FOR NO KEY UPDATE NOWAIT; + WITH RECURSIVE family(id) AS ( + SELECT id FROM workspace WHERE id = root_id AND forked_from_workspace_id IS NULL + UNION + SELECT w.id FROM workspace w JOIN family f ON w.forked_from_workspace_id = f.id + ) SELECT array_agg(id ORDER BY id) INTO current_ids FROM (SELECT id FROM family LIMIT 1001) bounded; + IF family_ids IS DISTINCT FROM current_ids THEN + RAISE EXCEPTION 'Project backfill lineage changed during discovery' USING ERRCODE = '55P03'; + END IF; + SELECT * INTO STRICT root FROM workspace WHERE id = root_id; + IF EXISTS (SELECT 1 FROM workspace WHERE id = ANY(family_ids) AND organization_id IS DISTINCT FROM root.organization_id) THEN + RAISE EXCEPTION 'Project backfill family spans organizations; reconcile before retrying' USING ERRCODE = '55000', DETAIL = root_id; + END IF; + SELECT array_agg(DISTINCT project_id ORDER BY project_id) INTO project_ids + FROM project_workspace WHERE workspace_id = ANY(family_ids); + IF cardinality(project_ids) > 1 THEN + RAISE EXCEPTION 'Project backfill family spans Projects; reconcile before retrying' USING ERRCODE = '55000', DETAIL = root_id; + END IF; + SELECT CASE WHEN count(*) = count(archived_at) THEN max(archived_at) END INTO archive_time + FROM workspace WHERE id = ANY(family_ids); + target_project := project_ids[1]; + IF target_project IS NOT NULL THEN + IF NOT pg_try_advisory_xact_lock(hashtextextended('project:' || target_project, 0)) THEN + RAISE EXCEPTION 'Project backfill Project is busy' USING ERRCODE = '55P03'; + END IF; + SELECT * INTO STRICT existing FROM project WHERE id = target_project FOR UPDATE NOWAIT; + IF existing.organization_id IS DISTINCT FROM root.organization_id + OR (existing.archived_at IS NULL) <> (archive_time IS NULL) + OR EXISTS (SELECT 1 FROM project_workspace WHERE project_id = target_project AND NOT workspace_id = ANY(family_ids)) THEN + RAISE EXCEPTION 'Project backfill existing assignment has incompatible scope, archive state, or lineage' USING ERRCODE = '55000', DETAIL = root_id; + END IF; + ELSE + target_project := gen_random_uuid()::text; + INSERT INTO project (id, name, owner_id, organization_id, archived_at) + VALUES (target_project, left(coalesce(nullif(btrim(root.name), ''), 'Untitled'), 90) || ' - Project', root.owner_id, root.organization_id, archive_time); + END IF; + INSERT INTO project_workspace (project_id, workspace_id) + SELECT target_project, id FROM unnest(family_ids) ids(id) + WHERE NOT EXISTS (SELECT 1 FROM project_workspace pw WHERE pw.workspace_id = ids.id); + GET DIAGNOSTICS inserted = ROW_COUNT; + assigned := assigned + inserted; + END IF; + EXCEPTION WHEN lock_not_available OR deadlock_detected OR serialization_failure THEN + retry := true; + END; + -- Commit outside the exception subtransaction, releasing every row/advisory lock before retry. + COMMIT; + IF retry THEN + attempts := attempts + 1; + IF attempts >= 20 THEN + RAISE EXCEPTION 'Project backfill family remains busy; safe to retry migration' USING ERRCODE = '55P03', DETAIL = root_id; + END IF; + PERFORM pg_sleep(0.05 + random() * 0.15); + COMMIT; + ELSE + attempts := 0; + completed := completed + 1; + previous_root := root_id; + IF completed % 100 = 0 THEN + RAISE NOTICE 'Project backfill: % families processed, % memberships assigned', completed, assigned; + END IF; + END IF; + END LOOP; + RAISE NOTICE 'Project backfill complete: % families processed, % memberships assigned', completed, assigned; +END; +$$; --> statement-breakpoint -SET LOCAL lock_timeout = '5s'; +CALL pg_temp.backfill_project_families(); --> statement-breakpoint -SET LOCAL statement_timeout = '60s'; +DROP PROCEDURE pg_temp.backfill_project_families(); +-- migration-safe: session-local scratch table created above; no application readers or persistent data. +DROP TABLE pg_temp.project_backfill_roots; --> statement-breakpoint -LOCK TABLE workspace, project, project_workspace, workflow IN SHARE ROW EXCLUSIVE MODE; +SET statement_timeout = '60s'; --> statement-breakpoint -DO $$ +CREATE OR REPLACE FUNCTION pg_temp.validate_project_membership() RETURNS void LANGUAGE plpgsql AS $$ BEGIN + IF EXISTS ( + WITH RECURSIVE reachable(id) AS ( + SELECT id FROM workspace WHERE forked_from_workspace_id IS NULL + UNION + SELECT w.id FROM workspace w JOIN reachable r ON w.forked_from_workspace_id = r.id + ) SELECT 1 FROM workspace w LEFT JOIN reachable r ON r.id = w.id WHERE r.id IS NULL + ) THEN + RAISE EXCEPTION 'Project backfill found a fork cycle or missing parent; reconcile before retrying' USING ERRCODE = '55000'; + END IF; IF EXISTS (SELECT 1 FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id WHERE pw.workspace_id IS NULL) THEN - RAISE EXCEPTION 'Project enforcement requires the reviewed Project backfill: missing environment memberships' USING ERRCODE = '55000'; + RAISE EXCEPTION 'Project enforcement found environments unreachable from a valid fork root or concurrently detached; reconcile and retry' USING ERRCODE = '55000'; END IF; IF EXISTS ( SELECT 1 FROM project p LEFT JOIN project_workspace pw ON pw.project_id = p.id @@ -18,7 +147,7 @@ BEGIN OR (p.archived_at IS NULL AND count(w.id) FILTER (WHERE w.archived_at IS NULL) = 0) OR (p.archived_at IS NOT NULL AND count(w.id) FILTER (WHERE w.archived_at IS NULL) > 0) ) THEN - RAISE EXCEPTION 'Project enforcement requires nonempty Projects with consistent archive state; reconcile and verify the backfill' USING ERRCODE = '55000'; + RAISE EXCEPTION 'Project enforcement requires nonempty Projects with consistent archive state; reconcile and retry the migration' USING ERRCODE = '55000'; END IF; IF EXISTS ( SELECT 1 FROM project_workspace pw JOIN project p ON p.id = pw.project_id @@ -40,6 +169,8 @@ BEGIN END; $$; --> statement-breakpoint +SELECT pg_temp.validate_project_membership(); +--> statement-breakpoint CREATE OR REPLACE FUNCTION project_contract_assert_project(target_id text) RETURNS void LANGUAGE plpgsql AS $$ DECLARE @@ -183,6 +314,15 @@ BEGIN END; $$; --> statement-breakpoint +BEGIN; +--> statement-breakpoint +SET LOCAL lock_timeout = '1s'; +--> statement-breakpoint +SET LOCAL statement_timeout = '5s'; +--> statement-breakpoint +-- Only trigger installation holds table locks. Both data scans run outside this transaction. +LOCK TABLE workspace, project, project_workspace, workflow IN ACCESS EXCLUSIVE MODE NOWAIT; +--> statement-breakpoint DROP TRIGGER IF EXISTS project_contract_lock ON project; CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OR DELETE ON project FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); @@ -212,3 +352,10 @@ CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OF works DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); --> statement-breakpoint COMMIT; +--> statement-breakpoint +-- Installed triggers keep new writes valid while this read-only scan checks existing rows. +SELECT pg_temp.validate_project_membership(); +--> statement-breakpoint +DROP FUNCTION pg_temp.validate_project_membership(); +--> statement-breakpoint +SET statement_timeout = 0; diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 970966c5521..03247cbfe51 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -4,7 +4,6 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { promisify } from 'node:util' import journal from '@sim/db/migrations/meta/_journal.json' -import { backfillProjects } from '@sim/db/project-backfill' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { createDeferred } from '@sim/testing/helpers/deferred' import { getPostgresErrorCode } from '@sim/utils/errors' @@ -51,29 +50,24 @@ async function database(run: (sql: Sql, url: string) => Promise) { async function enforce(sql: Sql) { const reserved = await sql.reserve() try { - await reserved.unsafe(migration) + for (const statement of migration.split('--> statement-breakpoint')) { + await reserved.unsafe(statement) + } } catch (error) { - await reserved.unsafe('ROLLBACK') + if (!['25P04', 'CONNECTION_CLOSED'].includes(getPostgresErrorCode(error) ?? '')) { + await reserved.unsafe('ROLLBACK') + } throw error } finally { reserved.release() } } -async function backfill(sql: Sql) { - const plan = await backfillProjects(sql, { mode: 'dry-run', databaseId: 'contract-test' }) - const applied = await backfillProjects(sql, { mode: 'apply', databaseId: 'contract-test', plan }) - expect(applied.conflicts).toEqual([]) - expect((await backfillProjects(sql, { mode: 'verify', databaseId: 'contract-test' })).ready).toBe( - true - ) -} - async function seed(sql: Sql) { await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Production', 'owner')` await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) VALUES ('child', 'Staging', 'owner', 'root')` await sql`INSERT INTO workflow VALUES ('flow', 'root', NULL)` - await backfill(sql) + await enforce(sql) } const constraintFailure = (error: unknown) => getPostgresErrorCode(error) === '23514' @@ -117,35 +111,37 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { ), }) ) - await database(async (sql) => { - const run = () => migrate(drizzle(sql), { migrationsFolder: directory }) - await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('legacy', 'Legacy', 'owner')` - await expect(run()).rejects.toSatisfy( - (error: unknown) => getPostgresErrorCode(error) === '55000' - ) - expect(await sql`SELECT * FROM drizzle.__drizzle_migrations`).toHaveLength(0) - await backfill(sql) - await run() - await run() - expect(await sql`SELECT * FROM drizzle.__drizzle_migrations`).toHaveLength(1) - await expect(sql`DELETE FROM project_workspace`).rejects.toSatisfy(constraintFailure) + await database(async (sql, url) => { + const runner = postgres(url, { max: 1, onnotice: () => undefined }) + const run = () => migrate(drizzle(runner), { migrationsFolder: directory }) + try { + await sql`INSERT INTO project (id, name, owner_id) VALUES ('empty', 'Empty', 'owner')` + await expect(run()).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55000' + ) + expect(await sql`SELECT * FROM drizzle.__drizzle_migrations`).toHaveLength(0) + await sql`DELETE FROM project WHERE id = 'empty'` + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('legacy', 'Legacy', 'owner')` + await run() + await run() + expect(await sql`SELECT * FROM drizzle.__drizzle_migrations`).toHaveLength(1) + await expect(sql`DELETE FROM project_workspace`).rejects.toSatisfy(constraintFailure) + } finally { + await runner.end() + } }) } finally { await rm(directory, { recursive: true, force: true }) } }) - it('refuses missing backfill and installs no partial enforcement, then accepts a verified backfill', async () => { + it('backfills legacy environments before installing mandatory membership', async () => { await database(async (sql) => { await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('legacy', 'Legacy', 'owner')` - await expect(enforce(sql)).rejects.toSatisfy( - (error: unknown) => getPostgresErrorCode(error) === '55000' - ) - expect( - await sql`SELECT 1 FROM pg_trigger WHERE tgname = 'project_contract_check'` - ).toHaveLength(0) - await backfill(sql) await enforce(sql) + expect(await sql`SELECT 1 FROM project_workspace WHERE workspace_id = 'legacy'`).toHaveLength( + 1 + ) await expect( sql`INSERT INTO workspace (id, name, owner_id) VALUES ('unassigned', 'Invalid', 'owner')` ).rejects.toSatisfy(constraintFailure) @@ -304,7 +300,8 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { it('refuses enforcement when an archived Project retains an active workflow', async () => { await database(async (sql) => { - await seed(sql) + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Root', 'owner')` + await sql`INSERT INTO workflow VALUES ('flow', 'root', NULL)` await sql`UPDATE workspace SET archived_at = now()` await sql`UPDATE project SET archived_at = now()` await expect(enforce(sql)).rejects.toSatisfy( @@ -314,4 +311,318 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await enforce(sql) }) }) + it('groups fork families, preserves partial assignments, and retains archived and detached Projects on replay', async () => { + await database(async (sql) => { + await sql`INSERT INTO "user" VALUES ('other-owner')` + await sql`INSERT INTO workspace (id, name, owner_id, organization_id, forked_from_workspace_id, archived_at) VALUES + ('a-root', 'Production', 'owner', 'org', NULL, NULL), + ('b-child', 'Staging', 'other-owner', 'org', 'a-root', NULL), + ('c-grandchild', 'Dev', 'owner', 'org', 'b-child', NULL), + ('d-detached', 'Detached', 'owner', NULL, NULL, NULL), + ('e-child', 'Child', 'owner', NULL, 'd-detached', NULL), + ('f-archived', 'Archive', 'owner', NULL, NULL, '2025-01-01'), + ('g-archived', 'Archive child', 'owner', NULL, 'f-archived', '2025-02-01')` + await sql`INSERT INTO project (id, name, owner_id, organization_id) VALUES ('existing', 'Keep name', 'owner', 'org')` + await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('existing', 'a-root')` + await enforce(sql) + expect( + await sql`SELECT workspace_id FROM project_workspace WHERE project_id = 'existing' ORDER BY workspace_id` + ).toEqual([ + { workspace_id: 'a-root' }, + { workspace_id: 'b-child' }, + { workspace_id: 'c-grandchild' }, + ]) + const projects = + await sql`SELECT id, name, owner_id, archived_at::text FROM project ORDER BY name` + expect( + projects.map(({ name, owner_id, archived_at }) => ({ name, owner_id, archived_at })) + ).toEqual([ + { name: 'Archive - Project', owner_id: 'owner', archived_at: '2025-02-01 00:00:00' }, + { name: 'Detached - Project', owner_id: 'owner', archived_at: null }, + { name: 'Keep name', owner_id: 'owner', archived_at: null }, + ]) + await enforce(sql) + expect( + await sql`SELECT id, name, owner_id, archived_at::text FROM project ORDER BY name` + ).toEqual(projects) + }) + }) + + it('keeps committed families on conflict and resumes without duplicating Projects after remediation', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id, name, owner_id, organization_id, forked_from_workspace_id) VALUES + ('a-good', 'Good', 'owner', NULL, NULL), + ('b-root', 'Root', 'owner', 'org', NULL), + ('b-child', 'Child', 'owner', NULL, 'b-root')` + await expect(enforce(sql)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55000' + ) + const committed = + await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'a-good'` + expect(committed).toHaveLength(1) + expect(await sql`SELECT 1 FROM project_workspace WHERE workspace_id LIKE 'b-%'`).toHaveLength( + 0 + ) + expect( + await sql`SELECT 1 FROM pg_trigger WHERE tgname = 'project_contract_check'` + ).toHaveLength(0) + await sql`UPDATE workspace SET organization_id = 'org' WHERE id = 'b-child'` + await enforce(sql) + expect( + await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'a-good'` + ).toEqual(committed) + expect(await sql`SELECT 1 FROM project`).toHaveLength(2) + expect(await sql`SELECT 1 FROM project_workspace`).toHaveLength(3) + }) + }) + + it('releases completed-family locks while a busy family retries and leaves unrelated writes available', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('a-complete', 'First', 'owner'), ('b-busy', 'Second', 'owner')` + const locked = createDeferred() + const release = createDeferred() + const writer = sql.begin(async (tx) => { + await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:b-busy', 0))` + locked.resolve() + await release.promise + await tx`UPDATE workspace SET name = 'Updated before backfill' WHERE id = 'b-busy'` + }) + await locked.promise + const migrationResult = enforce(sql).then( + () => null, + (error: unknown) => error + ) + try { + let committed = false + for (let attempt = 0; attempt < 100; attempt++) { + if ( + (await sql`SELECT 1 FROM project_workspace WHERE workspace_id = 'a-complete'`).length + ) { + committed = true + break + } + await sleep(10) + } + expect(committed).toBe(true) + expect( + await sql`SELECT 1 FROM project_workspace WHERE workspace_id = 'b-busy'` + ).toHaveLength(0) + await sql.begin(async (tx) => { + await tx`SET LOCAL statement_timeout = '500ms'` + await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:a-complete', 0))` + await tx`UPDATE workspace SET name = 'Unrelated live edit' WHERE id = 'a-complete'` + await tx`INSERT INTO workflow VALUES ('live-workflow', 'a-complete', NULL)` + }) + } finally { + release.resolve() + await writer + await migrationResult + } + expect(await migrationResult).toBeNull() + expect( + ( + await sql`SELECT p.name FROM project p JOIN project_workspace pw ON pw.project_id = p.id WHERE pw.workspace_id = 'b-busy'` + )[0].name + ).toBe('Updated before backfill - Project') + }) + }) + + it('re-discovers a fork added while the family is busy instead of committing partial membership', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Root', 'owner')` + const locked = createDeferred() + const release = createDeferred() + const writer = sql.begin(async (tx) => { + await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:root', 0))` + locked.resolve() + await release.promise + await tx`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) VALUES ('new-child', 'Child', 'owner', 'root')` + }) + await locked.promise + const migrationResult = enforce(sql).then( + () => null, + (error: unknown) => error + ) + try { + let retrying = false + for (let attempt = 0; attempt < 100; attempt++) { + if ( + ( + await sql`SELECT 1 FROM pg_stat_activity WHERE query LIKE '%CALL pg_temp.backfill_project_families%' AND wait_event = 'PgSleep'` + ).length + ) { + retrying = true + break + } + await sleep(10) + } + expect(retrying).toBe(true) + } finally { + release.resolve() + await writer + await migrationResult + } + expect(await migrationResult).toBeNull() + expect(await sql`SELECT DISTINCT project_id FROM project_workspace`).toHaveLength(1) + expect(await sql`SELECT 1 FROM project_workspace`).toHaveLength(2) + }) + }) + + it.each([ + 'cycle', + 'assigned-cycle', + 'split', + 'scope', + 'archive', + 'outside-family', + 'oversized', + ] as const)( + 'refuses %s legacy data without silently changing existing assignments', + async (scenario) => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) VALUES ('root', 'Root', 'owner', NULL), ('child', 'Child', 'owner', 'root')` + if (scenario === 'cycle' || scenario === 'assigned-cycle') { + if (scenario === 'assigned-cycle') { + await sql`INSERT INTO project (id, name, owner_id) VALUES ('existing', 'Existing', 'owner')` + await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('existing', 'root'), ('existing', 'child')` + } + await sql`UPDATE workspace SET forked_from_workspace_id = 'child' WHERE id = 'root'` + } else if (scenario === 'oversized') { + await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) SELECT 'extra-' || n, 'Extra', 'owner', 'root' FROM generate_series(1, 1000) n` + } else { + await sql`INSERT INTO project (id, name, owner_id) VALUES ('existing', 'Existing', 'owner')` + await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('existing', 'root')` + if (scenario === 'split') { + await sql`INSERT INTO project (id, name, owner_id) VALUES ('second', 'Second', 'owner')` + await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('second', 'child')` + } else if (scenario === 'scope') { + await sql`UPDATE project SET organization_id = 'org'` + } else if (scenario === 'archive') { + await sql`UPDATE project SET archived_at = now()` + } else { + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('outside', 'Outside', 'owner')` + await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('existing', 'outside')` + } + } + const before = await sql`SELECT * FROM project_workspace ORDER BY workspace_id` + await expect(enforce(sql)).rejects.toSatisfy( + (error: unknown) => + getPostgresErrorCode(error) === (scenario === 'oversized' ? '54000' : '55000') + ) + expect(await sql`SELECT * FROM project_workspace ORDER BY workspace_id`).toEqual(before) + }) + } + ) + + it('resumes after cancellation between committed families without retaining environment locks', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('a-first', 'First', 'owner'), ('b-busy', 'Busy', 'owner')` + const locked = createDeferred() + const release = createDeferred() + const writer = sql.begin(async (tx) => { + await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:b-busy', 0))` + locked.resolve() + await release.promise + }) + await locked.promise + const result = enforce(sql).then( + () => null, + (error: unknown) => error + ) + let committed: string | undefined + try { + for (let attempt = 0; attempt < 100; attempt++) { + const [row] = + await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'a-first'` + if (row) { + committed = row.project_id + break + } + await sleep(10) + } + expect(committed).toBeTruthy() + await sql`SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND query LIKE '%CALL pg_temp.backfill_project_families%'` + expect(getPostgresErrorCode(await result)).toBe('57014') + } finally { + release.resolve() + await writer + await result + } + await enforce(sql) + expect( + (await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'a-first'`)[0] + .project_id + ).toBe(committed) + expect(await sql`SELECT 1 FROM project_workspace`).toHaveLength(2) + }) + }) + + it('backfills a representative multi-family dataset through independently committed batches', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id, name, owner_id) SELECT 'root-' || n, 'Environment ' || n, 'owner' FROM generate_series(1, 100) n` + await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) + SELECT 'child-' || r || '-' || n, 'Child', 'owner', 'root-' || r FROM generate_series(1, 100) r CROSS JOIN generate_series(1, 20) n` + await enforce(sql) + expect((await sql`SELECT count(*)::int AS count FROM project`)[0].count).toBe(100) + expect((await sql`SELECT count(*)::int AS count FROM project_workspace`)[0].count).toBe(2100) + expect( + await sql`SELECT project_id FROM project_workspace GROUP BY project_id HAVING count(*) <> 21` + ).toHaveLength(0) + }) + }) + it('rolls back an overlong family while preserving earlier commits and safely resumes', async () => { + await database(async (sql, url) => { + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('a-fast', 'Fast', 'owner'), ('b-slow', 'Slow', 'owner')` + await sql.unsafe(` + CREATE FUNCTION delay_project_fixture() RETURNS trigger LANGUAGE plpgsql AS $$ + BEGIN + IF NEW.name = 'Slow - Project' THEN PERFORM pg_sleep(10); END IF; + RETURN NEW; + END; $$; + CREATE TRIGGER delay_project_fixture BEFORE INSERT ON project FOR EACH ROW EXECUTE FUNCTION delay_project_fixture(); + `) + const run = promisify(execFile)( + 'bun', + [ + '--no-env-file', + '-e', + ` + import { readFile } from 'node:fs/promises'; + import { getPostgresErrorCode } from '@sim/utils/errors'; + import postgres from 'postgres'; + const sql = postgres(process.env.TEST_DATABASE_URL, { max: 1, onnotice: () => undefined }); + try { + const migration = await readFile('migrations/0394_project_membership_enforcement.sql', 'utf8'); + for (const statement of migration.split('--> statement-breakpoint')) await sql.unsafe(statement); + await sql.end(); + } catch (error) { + process.stderr.write(getPostgresErrorCode(error) ?? 'unknown'); + process.exit(1); + } + `, + ], + { + cwd: new URL('..', import.meta.url), + env: { ...process.env, TEST_DATABASE_URL: url }, + timeout: 12000, + } + ) + await expect(run).rejects.toMatchObject({ + code: 1, + stderr: expect.stringMatching(/^(25P04|CONNECTION_CLOSED)$/), + }) + const committed = + await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'a-fast'` + expect(committed).toHaveLength(1) + expect(await sql`SELECT 1 FROM project_workspace WHERE workspace_id = 'b-slow'`).toHaveLength( + 0 + ) + await sql`DROP TRIGGER delay_project_fixture ON project` + await enforce(sql) + expect( + await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'a-fast'` + ).toEqual(committed) + expect(await sql`SELECT 1 FROM project_workspace`).toHaveLength(2) + }) + }, 15000) }) From 99189f2f643d754d2f80913dceb2dd33a37eda67 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 16:51:48 -0700 Subject: [PATCH 011/113] fix(projects): clean up automatically created fixture Projects --- .../__integration__/search-source-setup.integration.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts b/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts index eee347e4058..3d99262cb32 100644 --- a/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts @@ -6,6 +6,7 @@ import { knowledgeConnector, mcpServers, permissions, + project, resourcePolicy, user, workspace, @@ -96,6 +97,7 @@ describe('Search source identity and concurrent creation', () => { await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) await db.delete(workspace).where(eq(workspace.id, other.workspaceId)) for (const id of [ids.aliceId, ids.bobId, other.aliceId, other.bobId]) { + await db.delete(project).where(eq(project.ownerId, id)) await db.delete(user).where(eq(user.id, id)) } }) From 2ecbfdf62bdb632f386196728d266cec85b64f04 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 16:57:24 -0700 Subject: [PATCH 012/113] fix(projects): backfill only families missing membership --- .../0394_project_membership_enforcement.sql | 11 ++++++++-- .../scripts/project-contract.integration.ts | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/db/migrations/0394_project_membership_enforcement.sql b/packages/db/migrations/0394_project_membership_enforcement.sql index 4e080c4c6b5..a26bb15cb7c 100644 --- a/packages/db/migrations/0394_project_membership_enforcement.sql +++ b/packages/db/migrations/0394_project_membership_enforcement.sql @@ -6,7 +6,14 @@ SET statement_timeout = '15min'; --> statement-breakpoint CREATE TEMP TABLE IF NOT EXISTS project_backfill_roots (id text PRIMARY KEY) ON COMMIT PRESERVE ROWS; TRUNCATE project_backfill_roots; -INSERT INTO project_backfill_roots SELECT id FROM workspace WHERE forked_from_workspace_id IS NULL; +-- Only legacy families need assignment locks; existing complete Projects only need validation. +WITH RECURSIVE ancestors(id, parent_id) AS ( + SELECT w.id, w.forked_from_workspace_id FROM workspace w + LEFT JOIN project_workspace pw ON pw.workspace_id = w.id WHERE pw.workspace_id IS NULL + UNION + SELECT w.id, w.forked_from_workspace_id FROM workspace w JOIN ancestors a ON a.parent_id = w.id +) +INSERT INTO project_backfill_roots SELECT id FROM ancestors WHERE parent_id IS NULL; --> statement-breakpoint CREATE OR REPLACE PROCEDURE pg_temp.backfill_project_families() LANGUAGE plpgsql AS $$ DECLARE @@ -138,7 +145,7 @@ BEGIN RAISE EXCEPTION 'Project backfill found a fork cycle or missing parent; reconcile before retrying' USING ERRCODE = '55000'; END IF; IF EXISTS (SELECT 1 FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id WHERE pw.workspace_id IS NULL) THEN - RAISE EXCEPTION 'Project enforcement found environments unreachable from a valid fork root or concurrently detached; reconcile and retry' USING ERRCODE = '55000'; + RAISE EXCEPTION 'Project backfill found newly unassigned or detached environments; retry migration discovery' USING ERRCODE = '55P03'; END IF; IF EXISTS ( SELECT 1 FROM project p LEFT JOIN project_workspace pw ON pw.project_id = p.id diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 03247cbfe51..6b6af630581 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -625,4 +625,24 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { expect(await sql`SELECT 1 FROM project_workspace`).toHaveLength(2) }) }, 15000) + it.each(['separate-roots', 'large-family'] as const)( + 'preserves a complete existing Project with %s without putting it through legacy assignment', + async (shape) => { + await database(async (sql) => { + await sql`INSERT INTO project (id, name, owner_id) VALUES ('existing', 'Keep this Project', 'owner')` + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Root', 'owner')` + const count = shape === 'large-family' ? 1001 : 1 + await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) + SELECT 'other-' || n, 'Other', 'owner', ${shape === 'large-family' ? 'root' : null} FROM generate_series(1, ${count}) n` + await sql`INSERT INTO project_workspace (project_id, workspace_id) SELECT 'existing', id FROM workspace` + await enforce(sql) + expect(await sql`SELECT id, name FROM project`).toEqual([ + { id: 'existing', name: 'Keep this Project' }, + ]) + expect((await sql`SELECT count(*)::int AS count FROM project_workspace`)[0].count).toBe( + count + 1 + ) + }) + } + ) }) From 2afc6528db323fa67f2546f8090e4d74a33a2038 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 18:03:48 -0700 Subject: [PATCH 013/113] fix(projects): bound the trigger installation lock window --- .../0395_project_membership_enforcement.sql | 3 + .../scripts/project-contract.integration.ts | 78 +++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/packages/db/migrations/0395_project_membership_enforcement.sql b/packages/db/migrations/0395_project_membership_enforcement.sql index a26bb15cb7c..be912e7ec04 100644 --- a/packages/db/migrations/0395_project_membership_enforcement.sql +++ b/packages/db/migrations/0395_project_membership_enforcement.sql @@ -323,6 +323,9 @@ $$; --> statement-breakpoint BEGIN; --> statement-breakpoint +-- Bound the whole table-lock window, including time between client statements. +SET LOCAL transaction_timeout = '5s'; +--> statement-breakpoint SET LOCAL lock_timeout = '1s'; --> statement-breakpoint SET LOCAL statement_timeout = '5s'; diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 213d4a2b987..51a00b28ec1 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -135,6 +135,84 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { } }) + it('fails trigger installation promptly on a busy table and releases partially acquired locks', async () => { + await database(async (sql) => { + const held = createDeferred() + const release = createDeferred() + const reader = sql.begin(async (tx) => { + await tx`SELECT * FROM workflow` + held.resolve() + await release.promise + }) + await held.promise + try { + const started = performance.now() + await expect(enforce(sql)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55P03' + ) + expect(performance.now() - started).toBeLessThan(2000) + await sql.begin(async (tx) => { + await tx`SET LOCAL statement_timeout = '500ms'` + await tx`INSERT INTO workspace (id, name, owner_id) VALUES ('live', 'Live', 'owner')` + await tx`SELECT * FROM project` + }) + } finally { + release.resolve() + await reader + } + await enforce(sql) + expect(await sql`SELECT * FROM project_workspace WHERE workspace_id = 'live'`).toHaveLength(1) + }) + }) + + it('releases installation table locks when the migration client stalls between statements', async () => { + await database(async (sql, url) => { + const result = await promisify(execFile)( + 'bun', + [ + '--no-env-file', + '-e', + ` + import { readFile } from 'node:fs/promises'; + import { sleep } from '@sim/utils/helpers'; + import postgres from 'postgres'; + const sql = postgres(process.env.TEST_DATABASE_URL, { max: 1, onnotice: () => undefined }); + const observer = postgres(process.env.TEST_DATABASE_URL, { max: 1 }); + const [{ pid }] = await sql.unsafe('SELECT pg_backend_pid() AS pid'); + const locks = async () => (await observer.unsafe( + "SELECT count(*)::int AS count FROM pg_locks WHERE pid = $1 AND mode = 'AccessExclusiveLock' AND granted", + [pid] + ))[0].count; + const migration = await readFile('migrations/0395_project_membership_enforcement.sql', 'utf8'); + for (const statement of migration.split('--> statement-breakpoint')) { + await sql.unsafe(statement); + if (statement.includes('LOCK TABLE workspace,')) { + const before = await locks(); + await sleep(6500); + process.stdout.write(JSON.stringify({ before, after: await locks() })); + process.exit(0); + } + } + process.exit(1); + `, + ], + { + cwd: new URL('..', import.meta.url), + env: { ...process.env, TEST_DATABASE_URL: url }, + timeout: 12000, + } + ) + expect(JSON.parse(result.stdout)).toEqual({ before: 4, after: 0 }) + await sql.begin(async (tx) => { + await tx`SET LOCAL statement_timeout = '500ms'` + await tx`INSERT INTO workspace (id, name, owner_id) VALUES ('live', 'Live', 'owner')` + await tx`SELECT * FROM project` + }) + await enforce(sql) + expect(await sql`SELECT * FROM project_workspace WHERE workspace_id = 'live'`).toHaveLength(1) + }) + }, 15000) + it('backfills legacy environments before installing mandatory membership', async () => { await database(async (sql) => { await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('legacy', 'Legacy', 'owner')` From 6dfbc4fae2ae818a102c0386e7cc713ef2093c29 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 18:21:48 -0700 Subject: [PATCH 014/113] fix(workflows): guard restore against concurrent workspace archive --- .../__integration__/foundation.integration.ts | 41 +++++++++++++++++++ apps/sim/lib/workflows/lifecycle.ts | 3 ++ 2 files changed, 44 insertions(+) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 9f7e5397744..65c78c2ea13 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -47,6 +47,7 @@ import { lockWorkspaceProject, transferWorkspaceProjects, } from '@/lib/projects/membership' +import { restoreWorkflow } from '@/lib/workflows/lifecycle' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' import { createWorkspaceInTransaction } from '@/lib/workspaces/create' import { archiveWorkspace } from '@/lib/workspaces/lifecycle' @@ -940,6 +941,46 @@ describe('Project foundation at the database and application boundary', () => { } ) + check('workflow restore cannot overtake a concurrent Project archive', async () => { + const f = await fixture() + const workflowId = await addWorkflow(f.ids[0], f.ownerId) + await db.update(workflow).set({ archivedAt: new Date() }).where(eq(workflow.id, workflowId)) + const held = createDeferred() + const release = createDeferred() + const archive = db.transaction(async (tx) => { + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + await tx.select().from(workflow).where(eq(workflow.id, workflowId)).for('update') + await archiveProjectInTransaction(tx, f.projectId) + held.resolve(connection.pid) + await release.promise + }) + const blocker = await held.promise + const restore = restoreWorkflow(workflowId, request).then( + (result) => result, + (error: unknown) => error + ) + try { + let waiting = false + for (let attempt = 0; attempt < 100; attempt++) { + const rows = await db.execute(sql` + SELECT 1 FROM pg_stat_activity WHERE ${blocker} = ANY(pg_blocking_pids(pid)) + `) + if (rows.length) { + waiting = true + break + } + await sleep(10) + } + expect(waiting).toBe(true) + } finally { + release.resolve() + await archive + } + expect(await restore).toMatchObject({ code: 'not_found' }) + const [row] = await db.select().from(workflow).where(eq(workflow.id, workflowId)) + expect(row.archivedAt).not.toBeNull() + }) + check( 'Project archive rolls back as a unit and retries without losing environments', async () => { diff --git a/apps/sim/lib/workflows/lifecycle.ts b/apps/sim/lib/workflows/lifecycle.ts index 27e59772f40..d7a73c8fb7c 100644 --- a/apps/sim/lib/workflows/lifecycle.ts +++ b/apps/sim/lib/workflows/lifecycle.ts @@ -22,6 +22,7 @@ import { mcpPubSub } from '@/lib/mcp/pubsub' import { releaseWebhookPathClaims } from '@/lib/webhooks/path-claims' import { supersedeInFlightDeploymentOperations } from '@/lib/workflows/persistence/deployment-operations' import { getWorkflowById } from '@/lib/workflows/utils' +import { lockActiveWorkspace } from '@/lib/workspaces/active-workspace' const logger = createLogger('WorkflowLifecycle') @@ -178,6 +179,8 @@ export async function restoreWorkflow( const archivedAt = existingWorkflow.archivedAt await db.transaction(async (tx) => { + if (existingWorkflow.workspaceId) await lockActiveWorkspace(tx, existingWorkflow.workspaceId) + await tx .update(workflow) .set({ From 8965210697a90f6d4c75177b643c5069c614b6a7 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 18:21:58 -0700 Subject: [PATCH 015/113] fix(projects): keep workflow lifecycle guards at workspace scope --- .../knowledge-base-list.integration.ts | 3 +- apps/sim/tools/powerbi/powerbi.integration.ts | 12 ++-- .../0395_project_membership_enforcement.sql | 25 ++------ .../scripts/project-contract.integration.ts | 62 +++++++------------ 4 files changed, 37 insertions(+), 65 deletions(-) diff --git a/apps/sim/lib/knowledge/__integration__/knowledge-base-list.integration.ts b/apps/sim/lib/knowledge/__integration__/knowledge-base-list.integration.ts index f2e61a91623..d9d4c674adc 100644 --- a/apps/sim/lib/knowledge/__integration__/knowledge-base-list.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/knowledge-base-list.integration.ts @@ -1,6 +1,7 @@ import { writeFileSync } from 'node:fs' import { db } from '@sim/db' import { document, knowledgeBase, organization, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -126,7 +127,7 @@ afterAll(async () => { if (reportPath) writeFileSync(reportPath, JSON.stringify(reports, null, 2)) try { for (const fixture of [ids, foreign, manyBases]) { - await db.delete(workspace).where(eq(workspace.id, fixture.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, fixture.workspaceId)) await db.delete(organization).where(eq(organization.id, fixture.organizationId)) await db.delete(user).where(inArray(user.id, [fixture.aliceId, fixture.bobId])) } diff --git a/apps/sim/tools/powerbi/powerbi.integration.ts b/apps/sim/tools/powerbi/powerbi.integration.ts index 276f923b111..e3c1a55f45d 100644 --- a/apps/sim/tools/powerbi/powerbi.integration.ts +++ b/apps/sim/tools/powerbi/powerbi.integration.ts @@ -18,6 +18,7 @@ import { workflowExecutionSnapshots, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createBlock } from '@sim/testing/factories/block.factory' import { createDelegatedPrincipal, @@ -229,9 +230,12 @@ beforeAll(async () => { })) ) await db.insert(userStats).values({ id: generateId(), userId: ownerId }) - await db - .insert(workspace) - .values({ id: workspaceId, name: 'Power BI', ownerId, billedAccountUserId: ownerId }) + await insertWorkspaceFixture(db, { + id: workspaceId, + name: 'Power BI', + ownerId, + billedAccountUserId: ownerId, + }) await db.insert(permissions).values({ id: generateId(), userId: ownerId, @@ -310,7 +314,7 @@ afterAll(async () => { ) ) } - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(inArray(user.id, [ownerId, outsiderId])) await rm(path.join(UPLOAD_DIR_SERVER, 'execution', workspaceId), { recursive: true, diff --git a/packages/db/migrations/0395_project_membership_enforcement.sql b/packages/db/migrations/0395_project_membership_enforcement.sql index be912e7ec04..f0e79b93298 100644 --- a/packages/db/migrations/0395_project_membership_enforcement.sql +++ b/packages/db/migrations/0395_project_membership_enforcement.sql @@ -273,13 +273,8 @@ BEGIN IF TG_OP <> 'DELETE' THEN next_id := NEW.project_id; END IF; PERFORM project_contract_lock_projects(ARRAY[previous_id, next_id]); ELSE - IF TG_TABLE_NAME = 'workspace' THEN - IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; - IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; - ELSE - IF TG_OP <> 'INSERT' THEN previous_id := OLD.workspace_id; END IF; - IF TG_OP <> 'DELETE' THEN next_id := NEW.workspace_id; END IF; - END IF; + IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; SELECT array_agg(project_id ORDER BY workspace_id) INTO owners FROM project_workspace WHERE workspace_id IN (previous_id, next_id); PERFORM project_contract_lock_projects(owners); IF owners IS DISTINCT FROM ( @@ -310,12 +305,9 @@ BEGIN PERFORM project_contract_assert_project(NEW.project_id); PERFORM project_contract_assert_workspace(NEW.workspace_id); END IF; - ELSIF TG_TABLE_NAME = 'workspace' THEN + ELSE IF TG_OP <> 'INSERT' THEN PERFORM project_contract_assert_workspace(OLD.id); END IF; IF TG_OP <> 'DELETE' THEN PERFORM project_contract_assert_workspace(NEW.id); END IF; - ELSE - IF TG_OP <> 'INSERT' THEN PERFORM project_contract_assert_workspace(OLD.workspace_id); END IF; - IF TG_OP <> 'DELETE' THEN PERFORM project_contract_assert_workspace(NEW.workspace_id); END IF; END IF; RETURN NULL; END; @@ -331,7 +323,7 @@ SET LOCAL lock_timeout = '1s'; SET LOCAL statement_timeout = '5s'; --> statement-breakpoint -- Only trigger installation holds table locks. Both data scans run outside this transaction. -LOCK TABLE workspace, project, project_workspace, workflow IN ACCESS EXCLUSIVE MODE NOWAIT; +LOCK TABLE workspace, project, project_workspace IN ACCESS EXCLUSIVE MODE NOWAIT; --> statement-breakpoint DROP TRIGGER IF EXISTS project_contract_lock ON project; CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OR DELETE ON project @@ -354,16 +346,9 @@ DROP TRIGGER IF EXISTS project_contract_check ON workspace; CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OF id, archived_at, organization_id, forked_from_workspace_id OR DELETE ON workspace DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); --> statement-breakpoint -DROP TRIGGER IF EXISTS project_contract_lock ON workflow; -CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OF workspace_id, archived_at OR DELETE ON workflow -FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); -DROP TRIGGER IF EXISTS project_contract_check ON workflow; -CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OF workspace_id, archived_at OR DELETE ON workflow -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); ---> statement-breakpoint COMMIT; --> statement-breakpoint --- Installed triggers keep new writes valid while this read-only scan checks existing rows. +-- Membership triggers and compatible workspace-guarded writers protect new writes during validation. SELECT pg_temp.validate_project_membership(); --> statement-breakpoint DROP FUNCTION pg_temp.validate_project_membership(); diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 51a00b28ec1..c0783be5ca2 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -140,7 +140,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { const held = createDeferred() const release = createDeferred() const reader = sql.begin(async (tx) => { - await tx`SELECT * FROM workflow` + await tx`SELECT * FROM project_workspace` held.resolve() await release.promise }) @@ -202,7 +202,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { timeout: 12000, } ) - expect(JSON.parse(result.stdout)).toEqual({ before: 4, after: 0 }) + expect(JSON.parse(result.stdout)).toEqual({ before: 3, after: 0 }) await sql.begin(async (tx) => { await tx`SET LOCAL statement_timeout = '500ms'` await tx`INSERT INTO workspace (id, name, owner_id) VALUES ('live', 'Live', 'owner')` @@ -270,7 +270,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) - it('archives all Project environments and workflows together and refuses later active workflow insertion', async () => { + it('requires workflows to be archived when archiving their Project and environments', async () => { await database(async (sql) => { await seed(sql) await enforce(sql) @@ -285,9 +285,6 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await tx`UPDATE workspace SET archived_at = now()` await tx`UPDATE project SET archived_at = now()` }) - await expect(sql`INSERT INTO workflow VALUES ('late', 'child', NULL)`).rejects.toSatisfy( - constraintFailure - ) await expect( sql`UPDATE workspace SET archived_at = NULL WHERE id = 'root'` ).rejects.toSatisfy(constraintFailure) @@ -295,49 +292,34 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) - it('retries a workflow write when its environment moves Projects while the writer waits', async () => { + it('allows concurrent workflow lifecycle writes without rewriting or locking their Project', async () => { await database(async (sql) => { await seed(sql) - await enforce(sql) - const moved = createDeferred() + const before = await sql`SELECT id, xmin::text FROM project` + const held = createDeferred() const release = createDeferred() - const transfer = sql.begin(async (tx) => { - await tx`INSERT INTO project (id, name, owner_id) VALUES ('destination', 'Destination', 'owner')` - await tx`UPDATE workspace SET forked_from_workspace_id = NULL WHERE id = 'child'` - await tx`UPDATE project_workspace SET project_id = 'destination' WHERE workspace_id = 'child'` - moved.resolve() + const writer = sql.begin(async (tx) => { + await tx`INSERT INTO workflow VALUES ('first', 'root', NULL)` + held.resolve() await release.promise }) - await moved.promise - const writer = sql - .begin(async (tx) => { - await tx`SET LOCAL application_name = 'project_contract_stale_writer'` - await tx`INSERT INTO workflow VALUES ('racing', 'child', NULL)` - }) - .then( - () => null, - (error: unknown) => error - ) + await held.promise try { - let waiting = false - for (let attempt = 0; attempt < 100; attempt++) { - const rows = - await sql`SELECT 1 FROM pg_stat_activity WHERE application_name = 'project_contract_stale_writer' AND wait_event_type = 'Lock'` - if (rows.length) { - waiting = true - break - } - await sleep(10) - } - expect(waiting).toBe(true) + await sql.begin(async (tx) => { + await tx`SET LOCAL statement_timeout = '500ms'` + await tx`INSERT INTO workflow VALUES ('same-workspace', 'root', NULL), ('other-workspace', 'child', NULL)` + await tx`UPDATE workflow SET archived_at = now() WHERE id = 'other-workspace'` + await tx`UPDATE workflow SET workspace_id = 'child' WHERE id = 'same-workspace'` + await tx`DELETE FROM workflow WHERE id = 'other-workspace'` + }) } finally { release.resolve() + await writer } - await transfer - expect(getPostgresErrorCode(await writer)).toBe('40001') - expect(await sql`SELECT 1 FROM workflow WHERE id = 'racing'`).toHaveLength(0) - await sql`INSERT INTO workflow VALUES ('retried', 'child', NULL)` - expect(await sql`SELECT 1 FROM workflow WHERE id = 'retried'`).toHaveLength(1) + expect(await sql`SELECT id, xmin::text FROM project`).toEqual(before) + expect( + await sql`SELECT id FROM workflow WHERE id = 'same-workspace' AND workspace_id = 'child'` + ).toHaveLength(1) }) }) From 552ce688c39419a1836fbaebd47828cac4294630 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 2 Oct 2026 18:21:48 -0700 Subject: [PATCH 016/113] fix(workflows): guard restore against concurrent workspace archive --- .../__integration__/foundation.integration.ts | 41 +++++++++++++++++++ apps/sim/lib/workflows/lifecycle.ts | 3 ++ 2 files changed, 44 insertions(+) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 2512f49cb4d..a466df28eaf 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -45,6 +45,7 @@ import { lockWorkspaceProject, transferWorkspaceProjects, } from '@/lib/projects/membership' +import { restoreWorkflow } from '@/lib/workflows/lifecycle' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' import { createWorkspaceInTransaction } from '@/lib/workspaces/create' import { archiveWorkspace } from '@/lib/workspaces/lifecycle' @@ -838,6 +839,46 @@ describe('Project foundation at the database and application boundary', () => { } ) + check('workflow restore cannot overtake a concurrent Project archive', async () => { + const f = await fixture() + const workflowId = await addWorkflow(f.ids[0], f.ownerId) + await db.update(workflow).set({ archivedAt: new Date() }).where(eq(workflow.id, workflowId)) + const held = createDeferred() + const release = createDeferred() + const archive = db.transaction(async (tx) => { + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + await tx.select().from(workflow).where(eq(workflow.id, workflowId)).for('update') + await archiveProjectInTransaction(tx, f.projectId) + held.resolve(connection.pid) + await release.promise + }) + const blocker = await held.promise + const restore = restoreWorkflow(workflowId, request).then( + (result) => result, + (error: unknown) => error + ) + try { + let waiting = false + for (let attempt = 0; attempt < 100; attempt++) { + const rows = await db.execute(sql` + SELECT 1 FROM pg_stat_activity WHERE ${blocker} = ANY(pg_blocking_pids(pid)) + `) + if (rows.length) { + waiting = true + break + } + await sleep(10) + } + expect(waiting).toBe(true) + } finally { + release.resolve() + await archive + } + expect(await restore).toMatchObject({ code: 'not_found' }) + const [row] = await db.select().from(workflow).where(eq(workflow.id, workflowId)) + expect(row.archivedAt).not.toBeNull() + }) + check( 'Project archive rolls back as a unit and retries without losing environments', async () => { diff --git a/apps/sim/lib/workflows/lifecycle.ts b/apps/sim/lib/workflows/lifecycle.ts index 27e59772f40..d7a73c8fb7c 100644 --- a/apps/sim/lib/workflows/lifecycle.ts +++ b/apps/sim/lib/workflows/lifecycle.ts @@ -22,6 +22,7 @@ import { mcpPubSub } from '@/lib/mcp/pubsub' import { releaseWebhookPathClaims } from '@/lib/webhooks/path-claims' import { supersedeInFlightDeploymentOperations } from '@/lib/workflows/persistence/deployment-operations' import { getWorkflowById } from '@/lib/workflows/utils' +import { lockActiveWorkspace } from '@/lib/workspaces/active-workspace' const logger = createLogger('WorkflowLifecycle') @@ -178,6 +179,8 @@ export async function restoreWorkflow( const archivedAt = existingWorkflow.archivedAt await db.transaction(async (tx) => { + if (existingWorkflow.workspaceId) await lockActiveWorkspace(tx, existingWorkflow.workspaceId) + await tx .update(workflow) .set({ From 0b7eed6810ffe020e0bfda5e7fcd8d770bdde4cc Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sun, 4 Oct 2026 21:30:50 -0700 Subject: [PATCH 017/113] fix(projects): close lifecycle races and surface rollout conflicts --- .../app/api/folders/[id]/duplicate/route.ts | 4 +- .../api/superuser/import-workflow/route.ts | 24 +-- .../api/v1/admin/workflows/import/route.ts | 22 ++- .../v1/admin/workspaces/[id]/import/route.ts | 22 ++- apps/sim/app/api/workspaces/[id]/route.ts | 7 + .../components/project-issue-restrictions.tsx | 5 +- .../ee/workspace-forking/lib/create-fork.ts | 1 + apps/sim/hooks/queries/projects.ts | 3 + apps/sim/lib/api/contracts/user.ts | 1 + .../__integration__/foundation.integration.ts | 147 +++++++++++++- apps/sim/lib/projects/account-deletion.ts | 184 ++++++++++++------ .../projects/application/create-project.ts | 8 + apps/sim/lib/projects/membership.ts | 19 +- apps/sim/lib/users/account-deletion.ts | 15 +- apps/sim/lib/workflows/lifecycle.ts | 9 +- .../orchestration/workflow-lifecycle.ts | 7 +- .../lib/workflows/persistence/duplicate.ts | 6 +- .../workflows/persistence/new-workflow-row.ts | 4 +- apps/sim/lib/workspaces/admin-move.ts | 5 + apps/sim/lib/workspaces/lifecycle.ts | 16 +- 20 files changed, 397 insertions(+), 112 deletions(-) diff --git a/apps/sim/app/api/folders/[id]/duplicate/route.ts b/apps/sim/app/api/folders/[id]/duplicate/route.ts index cd0e6fea92c..84d529d2345 100644 --- a/apps/sim/app/api/folders/[id]/duplicate/route.ts +++ b/apps/sim/app/api/folders/[id]/duplicate/route.ts @@ -13,7 +13,7 @@ import { getSession } from '@/lib/auth' import { asOrchestrationError } from '@/lib/core/orchestration/types' import { generateRequestId } from '@/lib/core/utils/request' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' -import type { DbOrTx } from '@/lib/db/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { deduplicateFolderName } from '@/lib/folders/naming' import { nextFolderSortOrder } from '@/lib/folders/orchestration' import { assertFolderCollectionHasRoom, toFolderApi } from '@/lib/folders/queries' @@ -409,7 +409,7 @@ async function duplicateFolderStructure( } async function duplicateWorkflowsInFolderTree( - tx: DbOrTx, + tx: DbTransaction, sourceWorkspaceId: string, targetWorkspaceId: string, folderMapping: Map, diff --git a/apps/sim/app/api/superuser/import-workflow/route.ts b/apps/sim/app/api/superuser/import-workflow/route.ts index cc59d7627bf..355313556be 100644 --- a/apps/sim/app/api/superuser/import-workflow/route.ts +++ b/apps/sim/app/api/superuser/import-workflow/route.ts @@ -136,17 +136,19 @@ export const POST = withRouteHandler(async (request: NextRequest) => { null ) - await db.insert(workflow).values( - await buildNewWorkflowRow(db, { - id: newWorkflowId, - userId: session.user.id, - workspaceId: targetWorkspaceId, - folderId: null, - name: dedupedName, - description: sourceWorkflow.description, - variables: sourceWorkflow.variables || {}, - }) - ) + await db.transaction(async (tx) => { + await tx.insert(workflow).values( + await buildNewWorkflowRow(tx, { + id: newWorkflowId, + userId: session.user.id, + workspaceId: targetWorkspaceId, + folderId: null, + name: dedupedName, + description: sourceWorkflow.description, + variables: sourceWorkflow.variables || {}, + }) + ) + }) // Save using existing persistence logic const saveResult = await saveWorkflowToNormalizedTables(newWorkflowId, importedData, { diff --git a/apps/sim/app/api/v1/admin/workflows/import/route.ts b/apps/sim/app/api/v1/admin/workflows/import/route.ts index 0c7f0f3c405..7cd20d02840 100644 --- a/apps/sim/app/api/v1/admin/workflows/import/route.ts +++ b/apps/sim/app/api/v1/admin/workflows/import/route.ts @@ -115,16 +115,18 @@ export const POST = withRouteHandler( const workflowId = generateId() const dedupedName = await deduplicateWorkflowName(workflowName, workspaceId, folderId || null) - await db.insert(workflow).values( - await buildNewWorkflowRow(db, { - id: workflowId, - userId: workspaceData.ownerId, - workspaceId, - folderId: folderId || null, - name: dedupedName, - description: workflowDescription, - }) - ) + await db.transaction(async (tx) => { + await tx.insert(workflow).values( + await buildNewWorkflowRow(tx, { + id: workflowId, + userId: workspaceData.ownerId, + workspaceId, + folderId: folderId || null, + name: dedupedName, + description: workflowDescription, + }) + ) + }) /** * Same normalization the editor and the v1 import API run, via the one diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts index f19a1c072a7..44ee2523b7b 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts @@ -350,16 +350,18 @@ async function importSingleWorkflow( const workflowId = generateId() const dedupedName = await deduplicateWorkflowName(workflowName, workspaceId, targetFolderId) - await db.insert(workflow).values( - await buildNewWorkflowRow(db, { - id: workflowId, - userId: ownerId, - workspaceId, - folderId: targetFolderId, - name: dedupedName, - description: workflowData.metadata?.description || 'Imported via Admin API', - }) - ) + await db.transaction(async (tx) => { + await tx.insert(workflow).values( + await buildNewWorkflowRow(tx, { + id: workflowId, + userId: ownerId, + workspaceId, + folderId: targetFolderId, + name: dedupedName, + description: workflowData.metadata?.description || 'Imported via Admin API', + }) + ) + }) /** * Same normalization the editor, the v1 import API and the single-workflow diff --git a/apps/sim/app/api/workspaces/[id]/route.ts b/apps/sim/app/api/workspaces/[id]/route.ts index c58003d5fd2..afc662ad4b7 100644 --- a/apps/sim/app/api/workspaces/[id]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/route.ts @@ -7,6 +7,7 @@ import { deleteWorkspaceBodySchema, updateWorkspaceContract } from '@/lib/api/co import { parseRequest, validationErrorResponse } from '@/lib/api/server' import { getSession } from '@/lib/auth' import { changeWorkspaceStoragePayerInTx } from '@/lib/billing/storage/payer-transfer' +import { OrchestrationError, statusForOrchestrationError } from '@/lib/core/orchestration/types' import { captureServerEvent } from '@/lib/posthog/server' import { archiveWorkspace } from '@/lib/workspaces/lifecycle' @@ -316,6 +317,12 @@ export const DELETE = withRouteHandler( return NextResponse.json({ success: true }) } catch (error) { + if (error instanceof OrchestrationError) { + return NextResponse.json( + { error: error.message }, + { status: statusForOrchestrationError(error.code) } + ) + } logger.error(`Error deleting workspace ${workspaceId}:`, error) return NextResponse.json({ error: 'Failed to delete workspace' }, { status: 500 }) } diff --git a/apps/sim/ee/access-control/components/project-issue-restrictions.tsx b/apps/sim/ee/access-control/components/project-issue-restrictions.tsx index 6951f137d05..d2404c7b548 100644 --- a/apps/sim/ee/access-control/components/project-issue-restrictions.tsx +++ b/apps/sim/ee/access-control/components/project-issue-restrictions.tsx @@ -1,6 +1,7 @@ 'use client' import { Checkbox, Chip } from '@sim/emcn' +import { isApiClientError } from '@/lib/api/client/errors' import { useProjects } from '@/hooks/queries/projects' interface ProjectIssueRestrictionsProps { @@ -16,6 +17,9 @@ export function ProjectIssueRestrictions({ onChange, }: ProjectIssueRestrictionsProps) { const projects = useProjects(organizationId) + if (projects.isPending || (isApiClientError(projects.error) && projects.error.status === 503)) { + return null + } const selected = new Set(value) return (
@@ -24,7 +28,6 @@ export function ProjectIssueRestrictions({ For selected Projects, teammates governed by this group need access to every active environment to use Issues.

- {projects.isPending &&

Loading Projects…

} {projects.error && (

{projects.error.message}

)} diff --git a/apps/sim/ee/workspace-forking/lib/create-fork.ts b/apps/sim/ee/workspace-forking/lib/create-fork.ts index bc73206df66..995652275e4 100644 --- a/apps/sim/ee/workspace-forking/lib/create-fork.ts +++ b/apps/sim/ee/workspace-forking/lib/create-fork.ts @@ -233,6 +233,7 @@ export async function createFork(params: CreateForkParams): Promise page.nextCursor, staleTime: PROJECT_LIST_STALE_TIME, + retry: (failureCount, error) => + !(isApiClientError(error) && error.status === 503) && failureCount < 3, enabled: Boolean(organizationId), }) } diff --git a/apps/sim/lib/api/contracts/user.ts b/apps/sim/lib/api/contracts/user.ts index fdb25a3ddfa..458e2d736ea 100644 --- a/apps/sim/lib/api/contracts/user.ts +++ b/apps/sim/lib/api/contracts/user.ts @@ -477,6 +477,7 @@ export const accountDeletionBlockerSchema = z.object({ 'shared_workspace', 'organization_workspace', 'data_drain_owner', + 'project_lifecycle', ]), /** A sentence naming both the obstacle and the way out. */ message: z.string(), diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index a466df28eaf..2537873a504 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -42,10 +42,13 @@ import { import { archiveProjectInTransaction } from '@/lib/projects/lifecycle' import { createProjectForWorkspace, + lockProject, lockWorkspaceProject, + splitForkProject, transferWorkspaceProjects, } from '@/lib/projects/membership' -import { restoreWorkflow } from '@/lib/workflows/lifecycle' +import { getAccountDeletionPlan } from '@/lib/users/account-deletion' +import { disableUserResources, restoreWorkflow } from '@/lib/workflows/lifecycle' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' import { createWorkspaceInTransaction } from '@/lib/workspaces/create' import { archiveWorkspace } from '@/lib/workspaces/lifecycle' @@ -1083,6 +1086,148 @@ describe('Project foundation at the database and application boundary', () => { } ) + check( + 'account deletion preview reports a surviving Project losing its last active environment', + async () => { + const f = await fixture(false) + await db + .delete(permissions) + .where(and(eq(permissions.entityId, f.ids[0]), eq(permissions.userId, f.teammateId))) + await db + .delete(permissions) + .where(and(eq(permissions.entityId, f.ids[1]), eq(permissions.userId, f.ownerId))) + await db.insert(permissions).values({ + id: generateId(), + entityId: f.ids[1], + entityType: 'workspace', + userId: f.teammateId, + permissionType: 'admin', + }) + await db + .update(workspace) + .set({ archivedAt: new Date(), ownerId: f.teammateId, billedAccountUserId: f.teammateId }) + .where(eq(workspace.id, f.ids[1])) + const plan = await getAccountDeletionPlan(f.ownerId) + expect(plan.workspacesToDelete.map((row) => row.id)).toEqual([f.ids[0]]) + expect(plan.blockers).toEqual([ + { code: 'project_lifecycle', message: expect.stringContaining('Archive') }, + ]) + await expect( + db.transaction((tx) => prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[0]])) + ).rejects.toMatchObject({ code: 'conflict' }) + await db.transaction((tx) => archiveProjectInTransaction(tx, f.projectId)) + expect((await getAccountDeletionPlan(f.ownerId)).blockers).toEqual([]) + } + ) + + check( + 'account teardown can choose an organization admin without per-environment grants', + async () => { + const f = await fixture() + if (!f.organizationId) throw new Error('Missing organization fixture') + await db.insert(member).values({ + id: generateId(), + organizationId: f.organizationId, + userId: f.teammateId, + role: 'admin', + createdAt: new Date(), + }) + await db.transaction((tx) => prepareProjectsForAccountDeletion(tx, f.ownerId, [])) + const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(record.ownerId).toBe(f.teammateId) + } + ) + + check( + 'account teardown re-reads Projects created by an unlink while waiting for the old Project', + async () => { + const f = await fixture(false) + const held = createDeferred() + const release = createDeferred() + let detachedProjectId: string | null = null + const unlink = db.transaction(async (tx) => { + await lockProject(tx, f.projectId) + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + held.resolve(connection.pid) + await release.promise + detachedProjectId = await splitForkProject(tx, f.ids[1]) + await tx + .update(workspace) + .set({ forkedFromWorkspaceId: null }) + .where(eq(workspace.id, f.ids[1])) + }) + const blocker = await held.promise + const deletion = db.transaction((tx) => + prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[1]]) + ) + try { + let waiting = false + for (let attempt = 0; attempt < 100; attempt++) { + const rows = await db.execute( + sql`SELECT 1 FROM pg_stat_activity WHERE ${blocker} = ANY(pg_blocking_pids(pid))` + ) + if (rows.length) { + waiting = true + break + } + await sleep(10) + } + expect(waiting).toBe(true) + } finally { + release.resolve() + await unlink + } + await deletion + if (!detachedProjectId) throw new Error('Unlink did not create a Project') + expect(await db.select().from(project).where(eq(project.id, detachedProjectId))).toEqual([]) + } + ) + + check( + 'banning a former owner cannot archive environments transferred while waiting for the Project', + async () => { + const f = await fixture(false) + const held = createDeferred() + const release = createDeferred() + const transfer = db.transaction(async (tx) => { + await lockProject(tx, f.projectId) + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + held.resolve(connection.pid) + await release.promise + await tx + .update(workspace) + .set({ ownerId: f.teammateId }) + .where(inArray(workspace.id, f.ids)) + }) + const blocker = await held.promise + const ban = disableUserResources(f.ownerId) + try { + let waiting = false + for (let attempt = 0; attempt < 100; attempt++) { + const rows = await db.execute( + sql`SELECT 1 FROM pg_stat_activity WHERE ${blocker} = ANY(pg_blocking_pids(pid))` + ) + if (rows.length) { + waiting = true + break + } + await sleep(10) + } + expect(waiting).toBe(true) + } finally { + release.resolve() + await transfer + } + await ban + const rows = await db.select().from(workspace).where(inArray(workspace.id, f.ids)) + expect(rows.every((row) => row.archivedAt === null && row.ownerId === f.teammateId)).toBe( + true + ) + const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(record.archivedAt).toBeNull() + } + ) + check( 'account teardown transfers surviving Projects and atomically removes wholly private Projects', async () => { diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts index 98e14251a87..1831df0480d 100644 --- a/apps/sim/lib/projects/account-deletion.ts +++ b/apps/sim/lib/projects/account-deletion.ts @@ -1,24 +1,12 @@ -import { permissions, project, projectWorkspace, workspace } from '@sim/db/schema' +import { db } from '@sim/db' +import { member, permissions, project, projectWorkspace, workspace } from '@sim/db/schema' import { and, asc, eq, inArray, ne, or, sql } from 'drizzle-orm' import { OrchestrationError } from '@/lib/core/orchestration/types' -import type { DbTransaction } from '@/lib/db/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { lockProject, lockProjectBackfillWrites } from '@/lib/projects/membership' -/** Account teardown may erase a wholly private Project, but never strand a surviving one. */ -export async function prepareProjectsForAccountDeletion( - tx: DbTransaction, - userId: string, - doomedWorkspaceIds: string[] -): Promise { - const ownedEnvironments = await tx - .select({ id: workspace.id }) - .from(workspace) - .where(eq(workspace.ownerId, userId)) - await lockProjectBackfillWrites(tx, [ - ...doomedWorkspaceIds, - ...ownedEnvironments.map((row) => row.id), - ]) - const records = await tx +async function loadRelatedProjects(executor: DbOrTx, userId: string, doomedWorkspaceIds: string[]) { + return executor .select({ id: project.id }) .from(project) .where( @@ -33,59 +21,135 @@ export async function prepareProjectsForAccountDeletion( ) ) .orderBy(asc(project.id)) +} + +interface ProjectDeletionDecision { + blocker?: string + remove?: boolean + ownerId?: string +} + +async function planProjectDeletion( + executor: DbOrTx, + record: typeof project.$inferSelect, + userId: string, + doomed: Set +): Promise { + const members = await executor + .select({ id: workspace.id, archivedAt: workspace.archivedAt }) + .from(projectWorkspace) + .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) + .where(eq(projectWorkspace.projectId, record.id)) + const survivors = members.filter((row) => !doomed.has(row.id)) + if (!survivors.length) { + return record.organizationId || record.ownerId !== userId + ? { blocker: 'Account deletion cannot remove another owner’s Project' } + : { remove: true } + } + if (!record.archivedAt && survivors.every((row) => row.archivedAt)) { + return { + blocker: 'Archive the Project before deleting its last active environment with your account', + } + } + if (record.ownerId !== userId) return {} + if (record.organizationId) { + const [successor] = await executor + .select({ userId: member.userId }) + .from(member) + .where( + and( + eq(member.organizationId, record.organizationId), + ne(member.userId, userId), + inArray(member.role, ['owner', 'admin']) + ) + ) + .orderBy(asc(member.userId)) + .limit(1) + if (successor) return { ownerId: successor.userId } + } + const [successor] = await executor + .select({ userId: permissions.userId }) + .from(permissions) + .where( + and( + eq(permissions.entityType, 'workspace'), + eq(permissions.permissionType, 'admin'), + ne(permissions.userId, userId), + inArray( + permissions.entityId, + survivors.map((row) => row.id) + ) + ) + ) + .groupBy(permissions.userId) + .having(sql`count(*) = ${survivors.length}`) + .orderBy(asc(permissions.userId)) + .limit(1) + return successor + ? { ownerId: successor.userId } + : { + blocker: + 'Give a teammate admin access to every environment before deleting the Project owner’s account', + } +} + +/** Preview uses the same lifecycle and successor rules as the locked deletion transaction. */ +export async function getProjectAccountDeletionBlockers( + userId: string, + doomedWorkspaceIds: string[] +): Promise { + const records = await loadRelatedProjects(db, userId, doomedWorkspaceIds) + const doomed = new Set(doomedWorkspaceIds) + const blockers: string[] = [] + for (const { id } of records) { + const [record] = await db.select().from(project).where(eq(project.id, id)) + if (!record) continue + const decision = await planProjectDeletion(db, record, userId, doomed) + if (decision.blocker) blockers.push(decision.blocker) + } + return blockers +} + +/** Account teardown may erase a wholly private Project, but never strand a surviving one. */ +export async function prepareProjectsForAccountDeletion( + tx: DbTransaction, + userId: string, + doomedWorkspaceIds: string[] +): Promise { + const ownedEnvironments = await tx + .select({ id: workspace.id }) + .from(workspace) + .where(eq(workspace.ownerId, userId)) + await lockProjectBackfillWrites(tx, [ + ...doomedWorkspaceIds, + ...ownedEnvironments.map((row) => row.id), + ]) + const locked = new Set() + for (;;) { + const records = await loadRelatedProjects(tx, userId, doomedWorkspaceIds) + const pending = records.filter((row) => !locked.has(row.id)) + if (!pending.length) break + for (const { id } of pending) { + await lockProject(tx, id) + locked.add(id) + } + } + const records = await loadRelatedProjects(tx, userId, doomedWorkspaceIds) const doomed = new Set(doomedWorkspaceIds) for (const { id } of records) { await lockProject(tx, id) const [record] = await tx.select().from(project).where(eq(project.id, id)) if (!record) continue - const members = await tx - .select({ id: workspace.id, archivedAt: workspace.archivedAt }) - .from(projectWorkspace) - .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) - .where(eq(projectWorkspace.projectId, id)) - const survivors = members.filter((row) => !doomed.has(row.id)) - if (!survivors.length) { - if (record.organizationId || record.ownerId !== userId) - throw new OrchestrationError( - 'conflict', - 'Account deletion cannot remove another owner’s Project' - ) + const decision = await planProjectDeletion(tx, record, userId, doomed) + if (decision.blocker) throw new OrchestrationError('conflict', decision.blocker) + if (decision.remove) { await tx.delete(projectWorkspace).where(eq(projectWorkspace.projectId, id)) await tx.delete(project).where(eq(project.id, id)) - continue } - if (!record.archivedAt && survivors.every((row) => row.archivedAt)) - throw new OrchestrationError( - 'conflict', - 'Archive the Project before deleting its last active environment with your account' - ) - if (record.ownerId !== userId) continue - const admins = await tx - .select({ userId: permissions.userId }) - .from(permissions) - .where( - and( - eq(permissions.entityType, 'workspace'), - eq(permissions.permissionType, 'admin'), - ne(permissions.userId, userId), - inArray( - permissions.entityId, - survivors.map((row) => row.id) - ) - ) - ) - .groupBy(permissions.userId) - .having(sql`count(*) = ${survivors.length}`) - .orderBy(asc(permissions.userId)) - .limit(1) - if (!admins[0]) - throw new OrchestrationError( - 'conflict', - 'Give a teammate admin access to every environment before deleting the Project owner’s account' - ) + if (!decision.ownerId) continue await tx .update(project) - .set({ ownerId: admins[0].userId, updatedAt: new Date() }) + .set({ ownerId: decision.ownerId, updatedAt: new Date() }) .where(eq(project.id, id)) } } diff --git a/apps/sim/lib/projects/application/create-project.ts b/apps/sim/lib/projects/application/create-project.ts index b3d039b8e64..95baeca9441 100644 --- a/apps/sim/lib/projects/application/create-project.ts +++ b/apps/sim/lib/projects/application/create-project.ts @@ -88,6 +88,14 @@ export const createProject: OperationUseCase< getPostgresConstraintName(error) === 'workspace_owner_id_user_id_fk' ) throw new OrchestrationError('unauthorized', 'Unauthorized') + if ( + getPostgresErrorCode(error) === '23503' && + getPostgresConstraintName(error) === 'workspace_billed_account_user_id_user_id_fk' + ) + throw new OrchestrationError( + 'conflict', + 'The billing account changed; retry Project creation' + ) throw error } const environment = created.workspace diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index 5efe1592ecb..ed93c8059f0 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -103,13 +103,28 @@ export async function requireForkProject(tx: DbTransaction, parentWorkspaceId: s /** Legacy fallback must not hide partially assigned descendants. Caller holds the lineage lock. */ async function requireUnassignedForkSubtree(tx: DbTransaction, workspaceId: string): Promise { - const rows = await tx.execute<{ id: string }>(sql` + const descendants = await tx.execute<{ id: string }>(sql` WITH RECURSIVE descendants AS ( SELECT id FROM workspace WHERE id = ${workspaceId} UNION SELECT w.id FROM workspace w JOIN descendants d ON w.forked_from_workspace_id = d.id - ) SELECT d.id FROM descendants d JOIN project_workspace pw ON pw.workspace_id = d.id LIMIT 1 + ) SELECT id FROM descendants `) + if (!descendants.length) return + await lockProjectBackfillWrites( + tx, + descendants.map((row) => row.id) + ) + const rows = await tx + .select({ id: projectWorkspace.workspaceId }) + .from(projectWorkspace) + .where( + inArray( + projectWorkspace.workspaceId, + descendants.map((row) => row.id) + ) + ) + .limit(1) if (rows.length) throw new OrchestrationError( 'conflict', diff --git a/apps/sim/lib/users/account-deletion.ts b/apps/sim/lib/users/account-deletion.ts index 57f41ff7e79..ce66288f5d4 100644 --- a/apps/sim/lib/users/account-deletion.ts +++ b/apps/sim/lib/users/account-deletion.ts @@ -25,7 +25,10 @@ import type { import { getHighestPriorityPersonalSubscription } from '@/lib/billing/core/plan' import { isSoleOwnerOfPaidOrganization } from '@/lib/billing/organizations/membership' import { OrchestrationError } from '@/lib/core/orchestration/types' -import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' +import { + getProjectAccountDeletionBlockers, + prepareProjectsForAccountDeletion, +} from '@/lib/projects/account-deletion' import { appendTableEvent, type TableEvent } from '@/lib/table/events' import { type CancelledCellMarker, @@ -323,7 +326,7 @@ export async function getAccountDeletionPlan(userId: string): Promise 0, }) + const projectBlockers = await getProjectAccountDeletionBlockers( + userId, + plan.workspacesToDelete.map((row) => row.id) + ) + plan.blockers.push( + ...projectBlockers.map((message) => ({ code: 'project_lifecycle' as const, message })) + ) + return plan } interface StorageKeyRow { diff --git a/apps/sim/lib/workflows/lifecycle.ts b/apps/sim/lib/workflows/lifecycle.ts index d7a73c8fb7c..67d51ff0095 100644 --- a/apps/sim/lib/workflows/lifecycle.ts +++ b/apps/sim/lib/workflows/lifecycle.ts @@ -304,7 +304,6 @@ export async function disableUserResources(userId: string): Promise { '@/lib/projects/lifecycle' ) const { lockWorkspaceProject } = await import('@/lib/projects/membership') - const owned = new Set(ownedWorkspaces.map((row) => row.id)) const processed = new Set() for (const row of ownedWorkspaces) { if (processed.has(row.id)) continue @@ -312,18 +311,20 @@ export async function disableUserResources(userId: string): Promise { const record = await lockWorkspaceProject(tx, row.id) if (!record) return null const active = await tx - .select({ id: workspace.id }) + .select({ id: workspace.id, ownerId: workspace.ownerId }) .from(projectWorkspace) .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) .where(and(eq(projectWorkspace.projectId, record.id), isNull(workspace.archivedAt))) - if (!active.every((entry) => owned.has(entry.id))) return null + .orderBy(workspace.id) + .for('no key update', { of: workspace }) + if (!active.length || !active.every((entry) => entry.ownerId === userId)) return null return archiveProjectInTransaction(tx, record.id) }) if (archived) { for (const entry of archived.environments) processed.add(entry.id) await finishProjectArchive(archived, requestId) } else { - await archiveWorkspace(row.id, { requestId }) + await archiveWorkspace(row.id, { requestId, expectedOwnerId: userId }) processed.add(row.id) } } diff --git a/apps/sim/lib/workflows/orchestration/workflow-lifecycle.ts b/apps/sim/lib/workflows/orchestration/workflow-lifecycle.ts index d2be643a92f..7310f7544a3 100644 --- a/apps/sim/lib/workflows/orchestration/workflow-lifecycle.ts +++ b/apps/sim/lib/workflows/orchestration/workflow-lifecycle.ts @@ -8,7 +8,7 @@ import { generateId } from '@sim/utils/id' import { and, eq, isNull, ne } from 'drizzle-orm' import { OrchestrationError, type OrchestrationErrorCode } from '@/lib/core/orchestration/types' import { generateRequestId } from '@/lib/core/utils/request' -import type { DbOrTx } from '@/lib/db/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { archiveWorkflow, restoreWorkflow } from '@/lib/workflows/lifecycle' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' @@ -182,7 +182,10 @@ async function isWorkflowFolderInWorkspace( } /** Inserts only the workflow row so compound creation can commit its graph and receipt together. */ -export async function createWorkflowInTransaction(tx: DbOrTx, params: PerformCreateWorkflowParams) { +export async function createWorkflowInTransaction( + tx: DbTransaction, + params: PerformCreateWorkflowParams +) { const folderId = params.folderId ?? null if (!(await isWorkflowFolderInWorkspace(folderId, params.workspaceId, tx))) { throw new OrchestrationError('not_found', 'Target folder not found') diff --git a/apps/sim/lib/workflows/persistence/duplicate.ts b/apps/sim/lib/workflows/persistence/duplicate.ts index 94e90ed829e..09d486204a6 100644 --- a/apps/sim/lib/workflows/persistence/duplicate.ts +++ b/apps/sim/lib/workflows/persistence/duplicate.ts @@ -18,7 +18,7 @@ import { normalizeWorkflowEdgeTargetHandle, } from '@sim/workflow-types/workflow' import { and, eq } from 'drizzle-orm' -import type { DbOrTx } from '@/lib/db/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { remapConditionEdgeHandle } from '@/lib/workflows/condition-ids' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' import { @@ -51,7 +51,7 @@ interface DuplicateWorkflowOptions { * would require a second pooled connection while the caller's transaction * holds the first. */ - tx?: DbOrTx + tx?: DbTransaction workflowIdMap?: Map } @@ -161,7 +161,7 @@ export async function duplicateWorkflow( } } - const duplicateWithinTransaction = async (tx: DbOrTx) => { + const duplicateWithinTransaction = async (tx: DbTransaction) => { // First verify the source workflow exists const sourceWorkflowRow = await tx .select() diff --git a/apps/sim/lib/workflows/persistence/new-workflow-row.ts b/apps/sim/lib/workflows/persistence/new-workflow-row.ts index 36091a228f8..4b7324a8611 100644 --- a/apps/sim/lib/workflows/persistence/new-workflow-row.ts +++ b/apps/sim/lib/workflows/persistence/new-workflow-row.ts @@ -1,6 +1,6 @@ import { type workflow, workspace } from '@sim/db/schema' import { and, eq, isNull } from 'drizzle-orm' -import type { DbOrTx } from '@/lib/db/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { lockActiveWorkspace } from '@/lib/workspaces/active-workspace' interface NewWorkflowRowInput { @@ -40,7 +40,7 @@ export async function readForkSyncNewWorkflowsExcluded( * column default. A fork or promote copy is not new and is written by * `copyWorkflowStateIntoTarget` instead. */ -export async function buildNewWorkflowRow(executor: DbOrTx, input: NewWorkflowRowInput) { +export async function buildNewWorkflowRow(executor: DbTransaction, input: NewWorkflowRowInput) { const workspace = await lockActiveWorkspace(executor, input.workspaceId) const now = input.now ?? new Date() return { diff --git a/apps/sim/lib/workspaces/admin-move.ts b/apps/sim/lib/workspaces/admin-move.ts index 662ef5807ec..9ad81035fd1 100644 --- a/apps/sim/lib/workspaces/admin-move.ts +++ b/apps/sim/lib/workspaces/admin-move.ts @@ -30,6 +30,7 @@ import { planHasFixedSeatCap, resolveSeatCapacity, } from '@/lib/billing/validation/seat-management' +import { OrchestrationError } from '@/lib/core/orchestration/types' import { addOutboxEventSourceOperationId, enqueueOrReschedulePendingOutboxEvent, @@ -117,6 +118,7 @@ export class WorkspaceMoveError extends Error { | 'destination-entitlement-downgrade' | 'fork-lineage-conflict' | 'pending-invitations-present' + | 'project-conflict' ) { super(message) this.name = 'WorkspaceMoveError' @@ -1499,6 +1501,9 @@ export async function moveWorkspaceToOrganization(params: { }) break } catch (error) { + if (error instanceof OrchestrationError && error.code === 'conflict') { + throw new WorkspaceMoveError(error.message, 'project-conflict') + } if (error instanceof InvitationSetChangedError) { candidateInvitationIds = error.invitationIds continue diff --git a/apps/sim/lib/workspaces/lifecycle.ts b/apps/sim/lib/workspaces/lifecycle.ts index 078af37507d..08fa60e21c9 100644 --- a/apps/sim/lib/workspaces/lifecycle.ts +++ b/apps/sim/lib/workspaces/lifecycle.ts @@ -17,7 +17,7 @@ import { and, eq, inArray, isNull, sql } from 'drizzle-orm' import type { DbTransaction } from '@/lib/db/types' import { mcpPubSub } from '@/lib/mcp/pubsub' import { mcpService } from '@/lib/mcp/service' -import { requireRemainingProjectEnvironment } from '@/lib/projects/membership' +import { lockWorkspaceProject, requireRemainingProjectEnvironment } from '@/lib/projects/membership' import { archiveWorkflowsForWorkspace } from '@/lib/workflows/lifecycle' import { getWorkspaceWithOwner } from '@/lib/workspaces/permissions/utils' @@ -25,6 +25,7 @@ const logger = createLogger('WorkspaceLifecycle') interface ArchiveWorkspaceOptions { requestId: string + expectedOwnerId?: string } export async function archiveWorkspace( @@ -44,10 +45,21 @@ export async function archiveWorkspace( .from(workflowMcpServer) .where(eq(workflowMcpServer.workspaceId, workspaceId)) - await db.transaction(async (tx) => { + const archived = await db.transaction(async (tx) => { + if (options.expectedOwnerId) { + await lockWorkspaceProject(tx, workspaceId) + const [current] = await tx + .select({ ownerId: workspace.ownerId }) + .from(workspace) + .where(eq(workspace.id, workspaceId)) + .for('no key update') + if (!current || current.ownerId !== options.expectedOwnerId) return false + } await requireRemainingProjectEnvironment(tx, workspaceId) await archiveWorkspaceInTransaction(tx, workspaceId, now) + return true }) + if (!archived) return { archived: false } await archiveWorkflowsForWorkspace(workspaceId, options) From 634b19e500c97f30e0767684936883a3748705bf Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sun, 4 Oct 2026 21:52:04 -0700 Subject: [PATCH 018/113] fix(workflows): return not found when import loses archive race --- .../api/superuser/import-workflow/route.ts | 4 ++ .../api/v1/admin/workflows/import/route.ts | 4 ++ .../__integration__/foundation.integration.ts | 55 +++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/apps/sim/app/api/superuser/import-workflow/route.ts b/apps/sim/app/api/superuser/import-workflow/route.ts index 355313556be..6caaa1029fc 100644 --- a/apps/sim/app/api/superuser/import-workflow/route.ts +++ b/apps/sim/app/api/superuser/import-workflow/route.ts @@ -7,6 +7,7 @@ import { type NextRequest, NextResponse } from 'next/server' import { importWorkflowAsSuperuserContract } from '@/lib/api/contracts/workflows' import { parseRequest } from '@/lib/api/server' import { getSession } from '@/lib/auth' +import { OrchestrationError } from '@/lib/core/orchestration/types' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { loadCopilotChatMessages } from '@/lib/mothership/chat/lifecycle' import { appendCopilotChatMessages } from '@/lib/mothership/chat/messages-store' @@ -227,6 +228,9 @@ export const POST = withRouteHandler(async (request: NextRequest) => { copilotChatsImported, }) } catch (error) { + if (error instanceof OrchestrationError && error.code === 'not_found') { + return NextResponse.json({ error: 'Target workspace not found' }, { status: 404 }) + } logger.error('Error importing workflow', error) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } diff --git a/apps/sim/app/api/v1/admin/workflows/import/route.ts b/apps/sim/app/api/v1/admin/workflows/import/route.ts index 7cd20d02840..12a02a6c855 100644 --- a/apps/sim/app/api/v1/admin/workflows/import/route.ts +++ b/apps/sim/app/api/v1/admin/workflows/import/route.ts @@ -28,6 +28,7 @@ import { and, eq, isNull } from 'drizzle-orm' import { NextResponse } from 'next/server' import { adminV1ImportWorkflowContract } from '@/lib/api/contracts/v1/admin' import { parseRequest } from '@/lib/api/server' +import { OrchestrationError } from '@/lib/core/orchestration/types' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { parseWorkflowJson } from '@/lib/workflows/operations/import-export' import { buildNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' @@ -182,6 +183,9 @@ export const POST = withRouteHandler( return NextResponse.json(response) } catch (error) { + if (error instanceof OrchestrationError && error.code === 'not_found') { + return notFoundResponse('Workspace') + } if (error instanceof FolderNotFoundError) { return badRequestResponse(error.message) } diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 2537873a504..876079dc5d7 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -27,6 +27,7 @@ import { getErrorMessage, getPostgresErrorCode } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' +import { NextRequest } from 'next/server' import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' import { removeUserFromOrganization } from '@/lib/billing/organizations/membership' import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' @@ -55,9 +56,14 @@ import { archiveWorkspace } from '@/lib/workspaces/lifecycle' import { detachOrganizationWorkspacesTx } from '@/lib/workspaces/organization-workspaces' import { getWorkspaceWithOwner } from '@/lib/workspaces/permissions/utils' import { getWorkspaceCreationPolicy } from '@/lib/workspaces/policy' +import { POST as importAdminWorkflow } from '@/app/api/v1/admin/workflows/import/route' import { createFork } from '@/ee/workspace-forking/lib/create-fork' import { unlinkForkEdge } from '@/ee/workspace-forking/lib/lineage/unlink' +vi.hoisted(() => { + process.env.ADMIN_API_KEY = 'project-fixture-admin-key' +}) + beforeEach(() => { vi.stubEnv('PROJECT_API_ENABLED', 'true') }) @@ -842,6 +848,55 @@ describe('Project foundation at the database and application boundary', () => { } ) + check( + 'admin import returns not found when a concurrent archive wins the workspace lock', + async () => { + const f = await fixture(false, 1) + const held = createDeferred() + const release = createDeferred() + const archive = db.transaction(async (tx) => { + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + await archiveProjectInTransaction(tx, f.projectId) + held.resolve(connection.pid) + await release.promise + }) + const blocker = await held.promise + const imported = importAdminWorkflow( + new NextRequest('http://localhost:3000/api/v1/admin/workflows/import', { + method: 'POST', + headers: { + 'content-type': 'application/json', + 'x-admin-key': 'project-fixture-admin-key', + }, + body: JSON.stringify({ + workspaceId: f.ids[0], + workflow: { blocks: {}, edges: [], loops: {}, parallels: {} }, + }), + }), + {} + ) + try { + let waiting = false + for (let attempt = 0; attempt < 100; attempt++) { + const rows = await db.execute( + sql`SELECT 1 FROM pg_stat_activity WHERE ${blocker} = ANY(pg_blocking_pids(pid))` + ) + if (rows.length) { + waiting = true + break + } + await sleep(10) + } + expect(waiting).toBe(true) + } finally { + release.resolve() + await archive + } + expect((await imported).status).toBe(404) + expect(await db.select().from(workflow).where(eq(workflow.workspaceId, f.ids[0]))).toEqual([]) + } + ) + check('workflow restore cannot overtake a concurrent Project archive', async () => { const f = await fixture() const workflowId = await addWorkflow(f.ids[0], f.ownerId) From fd874d0b87185cb164fc40a83bc7568ed4a2b7f6 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sun, 4 Oct 2026 22:02:36 -0700 Subject: [PATCH 019/113] fix(projects): tighten rollout checks and deduplicate lifecycle validation --- .github/scripts/check-project-rollout.py | 30 +- .../desktop-tool-executor.integration.ts | 5 +- .../__integration__/foundation.integration.ts | 433 +++++++++--------- .../__integration__/fork-sync.integration.ts | 3 +- .../0395_project_membership_enforcement.sql | 47 +- .../scripts/project-contract.integration.ts | 43 ++ packages/db/testing/workspace-fixtures.ts | 13 + scripts/check-project-rollout.test.ts | 113 +++++ 8 files changed, 455 insertions(+), 232 deletions(-) create mode 100644 scripts/check-project-rollout.test.ts diff --git a/.github/scripts/check-project-rollout.py b/.github/scripts/check-project-rollout.py index 25aefd4beb7..9c0aab93b44 100644 --- a/.github/scripts/check-project-rollout.py +++ b/.github/scripts/check-project-rollout.py @@ -6,6 +6,7 @@ AWS checks below independently verify ECS retirement, not worker drainage. """ import argparse +import datetime import json import re import subprocess @@ -22,14 +23,31 @@ def aws(region, *args): return json.loads(result.stdout) +def latest_execution(region, pipeline): + executions = aws(region, 'codepipeline', 'list-pipeline-executions', '--pipeline-name', pipeline).get('pipelineExecutionSummaries', []) + def epoch(execution): + value = execution['startTime'] + return value if isinstance(value, (int, float)) else datetime.datetime.fromisoformat(value.replace('Z', '+00:00')).timestamp() + if not executions: + raise RuntimeError('No application deployment execution was found') + return max(executions, key=epoch) + + +def matches_release(execution, digest): + return execution.get('status') == 'Succeeded' and any( + revision.get('actionName') == 'ECR_Source' and revision.get('revisionId') == digest + for revision in execution.get('sourceRevisions', []) + ) + + def verify(environment, region, digest): if not re.fullmatch(r'sha256:[0-9a-f]{64}', digest): raise RuntimeError('Set the environment-specific PROJECT_ENFORCEMENT_READY_IMAGE_DIGEST after reviewing compatible rollout and worker-drain evidence') pipeline = f'sim-{environment}-{region}-app-deployment' - executions = aws(region, 'codepipeline', 'list-pipeline-executions', '--pipeline-name', pipeline).get('pipelineExecutionSummaries', []) - if not executions or executions[0].get('status') != 'Succeeded': - raise RuntimeError('The latest app pipeline has not completed; traffic cutover alone is insufficient') - execution_id = executions[0]['pipelineExecutionId'] + execution = latest_execution(region, pipeline) + if not matches_release(execution, digest): + raise RuntimeError('The latest app pipeline has not completed for the acknowledged image; traffic cutover alone is insufficient') + execution_id = execution['pipelineExecutionId'] group = aws(region, 'deploy', 'get-deployment-group', '--application-name', f'sim-{environment}-{region}-ecs-app', '--deployment-group-name', f'sim-{environment}-{region}-app-dg')['deploymentGroupInfo'] services = group.get('ecsServices', []) @@ -61,8 +79,8 @@ def verify(environment, region, digest): app = [container for container in task.get('containers', []) if container.get('name') == 'app'] if task.get('lastStatus') != 'RUNNING' or task.get('desiredStatus') != 'RUNNING' or len(app) != 1 or app[0].get('imageDigest') != digest: raise RuntimeError('A live ECS task does not match the acknowledged compatible release') - latest = aws(region, 'codepipeline', 'list-pipeline-executions', '--pipeline-name', pipeline).get('pipelineExecutionSummaries', []) - if not latest or latest[0].get('pipelineExecutionId') != execution_id or latest[0].get('status') != 'Succeeded': + latest = latest_execution(region, pipeline) + if latest.get('pipelineExecutionId') != execution_id or not matches_release(latest, digest): raise RuntimeError('Application deployment changed during preflight') print(json.dumps({'ecsRetired': True, 'expectedImageDigest': digest, 'pipelineExecutionId': execution_id, 'operatorAcknowledgedCompatibleWritersAndWorkers': True})) diff --git a/apps/sim/lib/mothership/tools/client/desktop-tool-executor.integration.ts b/apps/sim/lib/mothership/tools/client/desktop-tool-executor.integration.ts index 813a3be4e0e..e78e0bdead8 100644 --- a/apps/sim/lib/mothership/tools/client/desktop-tool-executor.integration.ts +++ b/apps/sim/lib/mothership/tools/client/desktop-tool-executor.integration.ts @@ -22,6 +22,7 @@ vi.mock('@/lib/auth', () => authMock) import { db } from '@sim/db' import { copilotAsyncToolCalls, copilotChats, copilotRuns, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' @@ -142,7 +143,7 @@ describe.runIf(Boolean(redisUrl))('a desktop tool call watched by a web tab', () createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Desktop executor fixture', ownerId: userId, @@ -177,7 +178,7 @@ describe.runIf(Boolean(redisUrl))('a desktop tool call watched by a web tab', () afterAll(async () => { await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index a12bdbf65b4..8a99b573f4b 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -18,7 +18,6 @@ import { workspaceForkPromoteRun, workspaceForkResourceMap, } from '@sim/db/schema' -import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { createSessionPrincipal, createWorkspaceApiKeyPrincipal, @@ -30,7 +29,7 @@ import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' import { NextRequest } from 'next/server' import postgres from 'postgres' -import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' import { removeUserFromOrganization } from '@/lib/billing/organizations/membership' import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' import { @@ -62,35 +61,87 @@ import { POST as importAdminWorkflow } from '@/app/api/v1/admin/workflows/import import { createFork } from '@/ee/workspace-forking/lib/create-fork' import { unlinkForkEdge } from '@/ee/workspace-forking/lib/lineage/unlink' -vi.hoisted(() => { +const isolated = await vi.hoisted(async () => { process.env.ADMIN_API_KEY = 'project-fixture-admin-key' + const { execFileSync } = await import('node:child_process') + const { fileURLToPath } = await import('node:url') + const { readTestDatabaseUrl } = await import('@sim/db/testing/test-infrastructure') + const { generateId } = await import('@sim/utils/id') + const { default: postgres } = await import('postgres') + const admin = postgres(readTestDatabaseUrl(), { max: 1 }) + const name = `project_foundation_test_${generateId().replaceAll('-', '')}` + const url = new URL(readTestDatabaseUrl()) + url.pathname = `/${name}` + const databaseUrl = url.toString() + await admin.unsafe(`CREATE DATABASE "${name}"`) + const client = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) + try { + await client.unsafe( + 'CREATE EXTENSION vector; CREATE EXTENSION btree_gin; CREATE EXTENSION pg_trgm' + ) + execFileSync('bun', ['--no-env-file', 'scripts/migrate.ts'], { + cwd: fileURLToPath(new URL('../../../../../packages/db/', import.meta.url)), + env: { ...process.env, DATABASE_URL: databaseUrl, MIGRATION_DATABASE_URL: databaseUrl }, + stdio: 'pipe', + timeout: 120_000, + }) + process.env.DATABASE_URL = databaseUrl + return { admin, name, databaseUrl, client } + } catch (error) { + await client.end() + await admin.unsafe(`DROP DATABASE "${name}" WITH (FORCE)`) + await admin.end() + throw error + } }) -beforeEach(() => { +beforeEach(async () => { vi.stubEnv('PROJECT_API_ENABLED', 'true') + const rows = await db.execute(sql`SELECT 1 FROM pg_trigger + WHERE tgname = 'project_contract_check' AND tgrelid IN ('project'::regclass, 'workspace'::regclass, 'project_workspace'::regclass)`) + expect(rows).toHaveLength(3) }) -let restoreEnforcement = false -beforeAll(async () => { - const rows = await db.execute( - sql`SELECT 1 FROM pg_trigger WHERE tgname = 'project_contract_check' AND tgrelid = 'public.workspace'::regclass` - ) - restoreEnforcement = rows.length > 0 -}) +async function enforce() { + const client = postgres(isolated.databaseUrl, { max: 1, onnotice: () => undefined }) + try { + const migration = await readFile( + new URL( + '../../../../../packages/db/migrations/0395_project_membership_enforcement.sql', + import.meta.url + ), + 'utf8' + ) + for (const statement of migration.split('--> statement-breakpoint')) + await client.unsafe(statement) + } finally { + await client.end() + } +} -const users: string[] = [] -const organizations: string[] = [] -const environments: string[] = [] const request = { requestId: 'project-foundation-integration', headers: new Headers() } const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = [] /** Exercises durable auth and lifecycle invariants against real Postgres, including concurrent writers. */ -function check(name: string, run: () => Promise) { +function check(name: string, run: () => Promise, legacy = false) { it(name, async () => { const started = performance.now() try { - await run() + if (legacy) { + try { + for (const table of ['project', 'project_workspace', 'workspace']) { + await isolated.client.unsafe( + `DROP TRIGGER project_contract_lock ON ${table}; DROP TRIGGER project_contract_check ON ${table}` + ) + } + await run() + } finally { + await enforce() + } + } else { + await run() + } checks.push({ name, status: 'passed', durationMs: performance.now() - started }) } catch (error) { checks.push({ @@ -110,7 +161,6 @@ async function fixture(org = true, count = 2) { const outsiderId = generateId() const now = new Date() for (const id of [ownerId, teammateId, outsiderId]) { - users.push(id) await db.insert(user).values({ id, email: `${id}@projects.invalid`, @@ -123,7 +173,6 @@ async function fixture(org = true, count = 2) { await db.insert(userStats).values({ id: generateId(), userId: ownerId }) const organizationId = org ? generateId() : null if (organizationId) { - organizations.push(organizationId) await db .insert(organization) .values({ id: organizationId, name: 'Project fixture', slug: organizationId, createdAt: now }) @@ -135,7 +184,6 @@ async function fixture(org = true, count = 2) { let projectId = '' await db.transaction(async (tx) => { for (const [index, id] of ids.entries()) { - environments.push(id) await tx.insert(workspace).values({ id, name: `Environment ${index}`, @@ -192,39 +240,18 @@ async function addWorkflow(workspaceId: string, userId: string) { } afterAll(async () => { - const reportPath = - process.env.PROJECT_FOUNDATION_REPORT_PATH ?? resolve('test-results/project-foundation.json') - await mkdir(dirname(reportPath), { recursive: true }) - await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) - if (environments.length) { - const memberships = await db - .select({ id: projectWorkspace.projectId }) - .from(projectWorkspace) - .where(inArray(projectWorkspace.workspaceId, environments)) - const ids = [...new Set(memberships.map((row) => row.id))] - await db.delete(projectWorkspace).where(inArray(projectWorkspace.workspaceId, environments)) - if (ids.length) await db.delete(project).where(inArray(project.id, ids)) - await db.delete(workspace).where(inArray(workspace.id, environments)) - } - if (organizations.length) - await db.delete(organization).where(inArray(organization.id, organizations)) - if (users.length) await db.delete(user).where(inArray(user.id, users)) - if (restoreEnforcement) { - const client = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => undefined }) + try { + const reportPath = + process.env.PROJECT_FOUNDATION_REPORT_PATH ?? resolve('test-results/project-foundation.json') + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) + } finally { + await db.$client.end({ timeout: 2 }) + await isolated.client.end() try { - for (const statement of ( - await readFile( - new URL( - '../../../../../packages/db/migrations/0395_project_membership_enforcement.sql', - import.meta.url - ), - 'utf8' - ) - ).split('--> statement-breakpoint')) { - await client.unsafe(statement) - } + await isolated.admin.unsafe(`DROP DATABASE "${isolated.name}" WITH (FORCE)`) } finally { - await client.end() + await isolated.admin.end() } } }) @@ -233,74 +260,52 @@ describe('Project foundation at the database and application boundary', () => { check( 'application creation, disconnect, organization deletion and archive commit with SQL enforcement', async () => { - const client = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => undefined }) - try { - for (const statement of ( - await readFile( - new URL( - '../../../../../packages/db/migrations/0395_project_membership_enforcement.sql', - import.meta.url - ), - 'utf8' - ) - ).split('--> statement-breakpoint')) { - await client.unsafe(statement) - } - const f = await fixture(true, 1) - const organizationId = f.organizationId - if (!organizationId) throw new Error('Missing organization fixture') - const created = await createProject.execute({ - principal: f.owner, - input: { - organizationId: f.organizationId, - name: 'Enforced', - initialEnvironment: { name: 'Production' }, - }, - request, - }) - environments.push(created.initialEnvironment.id) - const source = await getWorkspaceWithOwner(created.initialEnvironment.id) - if (!source) throw new Error('Missing source environment') - const fork = await createFork({ - source, - policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), - userId: f.ownerId, - name: 'Staging', - }) - environments.push(fork.workspace.id) - await unlinkForkEdge({ parentWorkspaceId: source.id, childWorkspaceId: fork.workspace.id }) - const [detached] = await db + const f = await fixture(true, 1) + const organizationId = f.organizationId + if (!organizationId) throw new Error('Missing organization fixture') + const created = await createProject.execute({ + principal: f.owner, + input: { + organizationId: f.organizationId, + name: 'Enforced', + initialEnvironment: { name: 'Production' }, + }, + request, + }) + + const source = await getWorkspaceWithOwner(created.initialEnvironment.id) + if (!source) throw new Error('Missing source environment') + const fork = await createFork({ + source, + policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), + userId: f.ownerId, + name: 'Staging', + }) + + await unlinkForkEdge({ parentWorkspaceId: source.id, childWorkspaceId: fork.workspace.id }) + const [detached] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) + expect(detached.projectId).not.toBe(created.project.id) + await db.transaction(async (tx) => { + await detachOrganizationWorkspacesTx(tx, organizationId) + await tx.delete(organization).where(eq(organization.id, organizationId)) + }) + await archiveProject.execute({ + principal: f.owner, + input: { projectId: created.project.id }, + request, + }) + const [archived] = await db.select().from(project).where(eq(project.id, created.project.id)) + expect(archived.organizationId).toBeNull() + expect(archived.archivedAt).not.toBeNull() + expect( + await db .select() - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) - expect(detached.projectId).not.toBe(created.project.id) - await db.transaction(async (tx) => { - await detachOrganizationWorkspacesTx(tx, organizationId) - await tx.delete(organization).where(eq(organization.id, organizationId)) - }) - await archiveProject.execute({ - principal: f.owner, - input: { projectId: created.project.id }, - request, - }) - const [archived] = await db.select().from(project).where(eq(project.id, created.project.id)) - expect(archived.organizationId).toBeNull() - expect(archived.archivedAt).not.toBeNull() - expect( - await db - .select() - .from(workflow) - .where(and(eq(workflow.workspaceId, source.id), sql`${workflow.archivedAt} IS NULL`)) - ).toHaveLength(0) - } finally { - await client.unsafe('ROLLBACK') - for (const table of ['project', 'project_workspace', 'workspace', 'workflow']) { - await client.unsafe( - `DROP TRIGGER IF EXISTS project_contract_lock ON ${table}; DROP TRIGGER IF EXISTS project_contract_check ON ${table}` - ) - } - await client.end() - } + .from(workflow) + .where(and(eq(workflow.workspaceId, source.id), sql`${workflow.archivedAt} IS NULL`)) + ).toHaveLength(0) } ) @@ -321,7 +326,7 @@ describe('Project foundation at the database and application boundary', () => { skipDefaultWorkflow: true, }) ) - environments.push(source.id) + expect( await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, source.id)) ).toHaveLength(1) @@ -333,7 +338,7 @@ describe('Project foundation at the database and application boundary', () => { userId: f.ownerId, name: 'Legacy child', }) - environments.push(fork.workspace.id) + expect( await db .select() @@ -404,7 +409,7 @@ describe('Project foundation at the database and application boundary', () => { userId: f.ownerId, name: 'Assigned child', }) - environments.push(fork.workspace.id) + const [membership] = await db .select() .from(projectWorkspace) @@ -437,37 +442,41 @@ describe('Project foundation at the database and application boundary', () => { skipDefaultWorkflow: true, }) ) - environments.push(created.id) + expect( await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, created.id)) ).toHaveLength(1) }) - check('legacy fork and disconnect refuse a partially assigned subtree', async () => { - const f = await fixture(false, 3) - await db - .delete(projectWorkspace) - .where(inArray(projectWorkspace.workspaceId, f.ids.slice(0, 2))) - vi.stubEnv('PROJECT_API_ENABLED', 'false') - const parent = await getWorkspaceWithOwner(f.ids[1]) - if (!parent) throw new Error('Missing source fixture') - await expect( - createFork({ - source: parent, - policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), - userId: f.ownerId, - name: 'Invalid child', - }) - ).rejects.toMatchObject({ code: 'conflict' }) - await expect( - unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: f.ids[1] }) - ).rejects.toMatchObject({ code: 'conflict' }) - const [child] = await db.select().from(workspace).where(eq(workspace.id, f.ids[1])) - expect(child.forkedFromWorkspaceId).toBe(f.ids[0]) - expect(await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId))).toHaveLength( - 3 - ) - }) + check( + 'legacy fork and disconnect refuse a partially assigned subtree', + async () => { + const f = await fixture(false, 3) + await db + .delete(projectWorkspace) + .where(inArray(projectWorkspace.workspaceId, f.ids.slice(0, 2))) + vi.stubEnv('PROJECT_API_ENABLED', 'false') + const parent = await getWorkspaceWithOwner(f.ids[1]) + if (!parent) throw new Error('Missing source fixture') + await expect( + createFork({ + source: parent, + policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), + userId: f.ownerId, + name: 'Invalid child', + }) + ).rejects.toMatchObject({ code: 'conflict' }) + await expect( + unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: f.ids[1] }) + ).rejects.toMatchObject({ code: 'conflict' }) + const [child] = await db.select().from(workspace).where(eq(workspace.id, f.ids[1])) + expect(child.forkedFromWorkspaceId).toBe(f.ids[0]) + expect( + await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId)) + ).toHaveLength(3) + }, + true + ) check( 'legacy membership decisions exclude backfill until the writing transaction commits', @@ -516,64 +525,66 @@ describe('Project foundation at the database and application boundary', () => { expect( await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) ).toHaveLength(1) - } + }, + true ) - check('a fork waiting for backfill inherits membership committed before it resumes', async () => { - const f = await fixture(false, 1) - await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) - await db.delete(project).where(eq(project.id, f.projectId)) - const parent = await getWorkspaceWithOwner(f.ids[0]) - if (!parent) throw new Error('Missing source fixture') - const policy = await getWorkspaceCreationPolicy({ userId: f.ownerId }) - const locked = createDeferred() - const release = createDeferred() - const backfill = db.transaction(async (tx) => { - await tx.execute( - sql`SELECT pg_advisory_xact_lock(hashtextextended(${`project-backfill:${f.ids[0]}`}, 0))` - ) - locked.resolve() - await release.promise - return createProjectForWorkspace(tx, { - workspaceId: f.ids[0], - name: 'Backfilled source', - organizationId: null, - ownerId: f.ownerId, + check( + 'a fork waiting for backfill inherits membership committed before it resumes', + async () => { + const f = await fixture(false, 1) + await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + await db.delete(project).where(eq(project.id, f.projectId)) + const parent = await getWorkspaceWithOwner(f.ids[0]) + if (!parent) throw new Error('Missing source fixture') + const policy = await getWorkspaceCreationPolicy({ userId: f.ownerId }) + const locked = createDeferred() + const release = createDeferred() + const backfill = db.transaction(async (tx) => { + await tx.execute( + sql`SELECT pg_advisory_xact_lock(hashtextextended(${`project-backfill:${f.ids[0]}`}, 0))` + ) + locked.resolve() + await release.promise + return createProjectForWorkspace(tx, { + workspaceId: f.ids[0], + name: 'Backfilled source', + organizationId: null, + ownerId: f.ownerId, + }) }) - }) - await Promise.race([locked.promise, backfill]) - const fork = createFork({ - source: parent, - policy, - userId: f.ownerId, - name: 'Concurrent child', - }).then((result) => { - environments.push(result.workspace.id) - return result - }) - let blocked = false - try { - for (let attempt = 0; attempt < 100; attempt++) { - const rows = await db.execute<{ waiting: boolean }>(sql`SELECT EXISTS ( + await Promise.race([locked.promise, backfill]) + const fork = createFork({ + source: parent, + policy, + userId: f.ownerId, + name: 'Concurrent child', + }) + let blocked = false + try { + for (let attempt = 0; attempt < 100; attempt++) { + const rows = await db.execute<{ waiting: boolean }>(sql`SELECT EXISTS ( SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND mode = 'ShareLock' AND NOT granted ) AS waiting`) - if (rows[0]?.waiting) { - blocked = true - break + if (rows[0]?.waiting) { + blocked = true + break + } + await sleep(20) } - await sleep(20) + } finally { + release.resolve() } - } finally { - release.resolve() - } - const [projectId, result] = await Promise.all([backfill, fork]) - expect(blocked).toBe(true) - const [membership] = await db - .select() - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, result.workspace.id)) - expect(membership.projectId).toBe(projectId) - }) + const [projectId, result] = await Promise.all([backfill, fork]) + expect(blocked).toBe(true) + const [membership] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, result.workspace.id)) + expect(membership.projectId).toBe(projectId) + }, + true + ) check( 'Project creation commits its named first environment and starter workflow in the requested scope', @@ -598,7 +609,7 @@ describe('Project foundation at the database and application boundary', () => { }, request, }) - environments.push(result.initialEnvironment.id) + const [created] = await db.select().from(project).where(eq(project.id, result.project.id)) expect(created).toMatchObject({ name: 'Customer support', @@ -755,7 +766,7 @@ describe('Project foundation at the database and application boundary', () => { skipDefaultWorkflow: true, }) ) - environments.push(source.id) + const before = await db .select() .from(projectWorkspace) @@ -770,7 +781,7 @@ describe('Project foundation at the database and application boundary', () => { userId: f.ownerId, name: 'Child environment', }) - environments.push(fork.workspace.id) + const child = await db .select() .from(projectWorkspace) @@ -1183,7 +1194,8 @@ describe('Project foundation at the database and application boundary', () => { expect(child.projectId).toBe(f.projectId) const [edge] = await db.select().from(workspace).where(eq(workspace.id, f.ids[1])) expect(edge.forkedFromWorkspaceId).toBe(f.ids[0]) - } + }, + true ) check( @@ -1314,9 +1326,10 @@ describe('Project foundation at the database and application boundary', () => { .where(eq(workspace.id, f.ids[1])) }) const blocker = await held.promise - const deletion = db.transaction((tx) => - prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[1]]) - ) + const deletion = db.transaction(async (tx) => { + await prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[1]]) + await tx.delete(workspace).where(eq(workspace.id, f.ids[1])) + }) try { let waiting = false for (let attempt = 0; attempt < 100; attempt++) { @@ -1396,13 +1409,17 @@ describe('Project foundation at the database and application boundary', () => { entityId: f.ids[1], permissionType: 'admin', }) - await db.transaction((tx) => prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[0]])) + await db.transaction(async (tx) => { + await prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[0]]) + await tx.delete(workspace).where(eq(workspace.id, f.ids[0])) + }) const [survivor] = await db.select().from(project).where(eq(project.id, f.projectId)) expect(survivor.ownerId).toBe(f.teammateId) const privateProject = await fixture(false, 1) - await db.transaction((tx) => - prepareProjectsForAccountDeletion(tx, privateProject.ownerId, privateProject.ids) - ) + await db.transaction(async (tx) => { + await prepareProjectsForAccountDeletion(tx, privateProject.ownerId, privateProject.ids) + await tx.delete(workspace).where(inArray(workspace.id, privateProject.ids)) + }) expect( await db.select().from(project).where(eq(project.id, privateProject.projectId)) ).toEqual([]) diff --git a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts index 03d97c559db..cfc27bfbe50 100644 --- a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts +++ b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts @@ -1213,8 +1213,9 @@ describe('authorized fork and sync against PostgreSQL', () => { createdWorkspaceIds.push(sourceWorkspaceId, childWorkspaceId) await insertWorkspaceFixture( db, - [sourceWorkspaceId, childWorkspaceId].map((id) => ({ + [childWorkspaceId, sourceWorkspaceId].map((id) => ({ id, + forkedFromWorkspaceId: id === childWorkspaceId ? sourceWorkspaceId : null, name: 'Knowledge copy fixture', ownerId: userId, billedAccountUserId: userId, diff --git a/packages/db/migrations/0395_project_membership_enforcement.sql b/packages/db/migrations/0395_project_membership_enforcement.sql index f0e79b93298..49a8ca091c7 100644 --- a/packages/db/migrations/0395_project_membership_enforcement.sql +++ b/packages/db/migrations/0395_project_membership_enforcement.sql @@ -240,7 +240,6 @@ BEGIN RAISE EXCEPTION 'Connected fork environments must belong to the same Project' USING ERRCODE = '23514', CONSTRAINT = 'workspace_fork_project'; END IF; - PERFORM project_contract_assert_project(member_project_id); END; $$; --> statement-breakpoint @@ -251,8 +250,6 @@ DECLARE BEGIN FOR target_id IN SELECT DISTINCT id FROM unnest(target_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP PERFORM pg_advisory_xact_lock(hashtextextended('project:' || target_id, 0)); - -- Touch the row so repeatable-read transactions serialize rather than accepting a stale environment count. - UPDATE project SET updated_at = updated_at WHERE id = target_id; END LOOP; END; $$; @@ -292,23 +289,43 @@ $$; --> statement-breakpoint CREATE OR REPLACE FUNCTION project_contract_after_write() RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE + previous_id text; + next_id text; + target_ids text[]; + workspace_ids text[]; + target_id text; BEGIN IF TG_TABLE_NAME = 'project' THEN - IF TG_OP <> 'INSERT' THEN PERFORM project_contract_assert_project(OLD.id); END IF; - IF TG_OP <> 'DELETE' THEN PERFORM project_contract_assert_project(NEW.id); END IF; - ELSIF TG_TABLE_NAME = 'project_workspace' THEN - IF TG_OP <> 'INSERT' THEN - PERFORM project_contract_assert_project(OLD.project_id); - PERFORM project_contract_assert_workspace(OLD.workspace_id); - END IF; - IF TG_OP <> 'DELETE' THEN - PERFORM project_contract_assert_project(NEW.project_id); - PERFORM project_contract_assert_workspace(NEW.workspace_id); + -- Only the final row version needs a full scan; earlier deferred events describe superseded states. + IF TG_OP <> 'DELETE' AND EXISTS (SELECT 1 FROM project WHERE id = NEW.id AND ctid = NEW.ctid) THEN + PERFORM project_contract_assert_project(NEW.id); END IF; + RETURN NULL; + END IF; + IF TG_TABLE_NAME = 'project_workspace' THEN + IF TG_OP <> 'INSERT' THEN previous_id := OLD.project_id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.project_id; END IF; + target_ids := ARRAY[previous_id, next_id]; + previous_id := NULL; + next_id := NULL; + IF TG_OP <> 'INSERT' THEN previous_id := OLD.workspace_id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.workspace_id; END IF; + workspace_ids := ARRAY[previous_id, next_id]; ELSE - IF TG_OP <> 'INSERT' THEN PERFORM project_contract_assert_workspace(OLD.id); END IF; - IF TG_OP <> 'DELETE' THEN PERFORM project_contract_assert_workspace(NEW.id); END IF; + IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; + workspace_ids := ARRAY[previous_id, next_id]; + SELECT array_agg(project_id) INTO target_ids FROM project_workspace + WHERE workspace_id = ANY(workspace_ids); END IF; + FOR target_id IN SELECT DISTINCT id FROM unnest(target_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP + -- Touch after the mutation: immediate constraints must see it too, and repeatable-read must detect stale snapshots. + UPDATE project SET updated_at = updated_at WHERE id = target_id; + END LOOP; + FOR target_id IN SELECT DISTINCT id FROM unnest(workspace_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP + PERFORM project_contract_assert_workspace(target_id); + END LOOP; RETURN NULL; END; $$; diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index c0783be5ca2..ef8242daa87 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -73,6 +73,49 @@ async function seed(sql: Sql) { const constraintFailure = (error: unknown) => getPostgresErrorCode(error) === '23514' describe('Project expand/backfill/contract against PostgreSQL', () => { + it('validates a bulk archive once per final Project row version', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Root', 'owner')` + await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) + SELECT 'child-' || n, 'Child', 'owner', 'root' FROM generate_series(1, 200) n` + await enforce(sql) + await sql.unsafe(` + CREATE SEQUENCE project_validation_count; + ALTER FUNCTION project_contract_assert_project(text) RENAME TO measured_project_assert; + CREATE FUNCTION project_contract_assert_project(target_id text) RETURNS void LANGUAGE plpgsql AS $$ + BEGIN + PERFORM nextval('project_validation_count'); + PERFORM measured_project_assert(target_id); + END $$; + `) + await sql.begin(async (tx) => { + await tx`UPDATE workspace SET archived_at = now()` + await tx`UPDATE project SET archived_at = now()` + }) + const [calls] = await sql`SELECT last_value::int AS count FROM project_validation_count` + expect(calls.count).toBe(1) + const [state] = + await sql`SELECT count(*)::int AS count FROM workspace WHERE archived_at IS NOT NULL` + expect(state.count).toBe(201) + }) + }) + + it('revalidates each mutation after constraints switch to immediate mode', async () => { + await database(async (sql) => { + await seed(sql) + await expect( + sql.begin(async (tx) => { + await tx`UPDATE workspace SET archived_at = now() WHERE id = 'root'` + await tx`SET CONSTRAINTS ALL IMMEDIATE` + await tx`UPDATE workspace SET archived_at = now() WHERE id = 'child'` + }) + ).rejects.toSatisfy(constraintFailure) + const [active] = + await sql`SELECT count(*)::int AS count FROM workspace WHERE archived_at IS NULL` + expect(active.count).toBe(2) + }) + }) + it('requires rollout evidence only for an existing database with enforcement pending', async () => { await database(async (sql, url) => { const run = () => diff --git a/packages/db/testing/workspace-fixtures.ts b/packages/db/testing/workspace-fixtures.ts index b23af0b0968..e1cb89fb958 100644 --- a/packages/db/testing/workspace-fixtures.ts +++ b/packages/db/testing/workspace-fixtures.ts @@ -18,7 +18,17 @@ export async function insertWorkspaceFixture( .insert(workspace) .values(Array.isArray(values) ? values : [values]) .returning() + const byId = new Map(rows.map((row) => [row.id, row])) + const children = new Map() + const ordered: typeof rows = [] for (const row of rows) { + if (row.forkedFromWorkspaceId && byId.has(row.forkedFromWorkspaceId)) { + const siblings = children.get(row.forkedFromWorkspaceId) ?? [] + siblings.push(row) + children.set(row.forkedFromWorkspaceId, siblings) + } else ordered.push(row) + } + for (const row of ordered) { const [parent] = row.forkedFromWorkspaceId ? await tx .select() @@ -35,7 +45,9 @@ export async function insertWorkspaceFixture( archivedAt: row.archivedAt, }) await tx.insert(projectWorkspace).values({ projectId, workspaceId: row.id }) + ordered.push(...(children.get(row.id) ?? [])) } + if (ordered.length !== rows.length) throw new Error('Workspace fixture contains a fork cycle') return rows }) } @@ -45,6 +57,7 @@ export async function deleteWorkspaceFixture( database: FixtureDatabase | FixtureTransaction, condition: SQL | undefined ) { + if (!condition) throw new Error('Workspace fixture deletion requires a predicate') await database.transaction(async (tx) => { const rows = await tx .select({ projectId: projectWorkspace.projectId }) diff --git a/scripts/check-project-rollout.test.ts b/scripts/check-project-rollout.test.ts new file mode 100644 index 00000000000..95bdaf5015c --- /dev/null +++ b/scripts/check-project-rollout.test.ts @@ -0,0 +1,113 @@ +import { spawnSync } from 'node:child_process' +import { mkdtemp, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { delimiter, join, resolve } from 'node:path' +import { describe, expect, it } from 'vitest' + +const digest = `sha256:${'a'.repeat(64)}` +const otherDigest = `sha256:${'b'.repeat(64)}` +const execution = (id: string, time: number, revision: string, status = 'Succeeded') => ({ + pipelineExecutionId: id, + startTime: new Date(time * 1000).toISOString(), + status, + sourceRevisions: [{ actionName: 'ECR_Source', revisionId: revision }], +}) +const current = execution('current', 2, digest) +const older = execution('older', 1, otherDigest) +const pending = execution('pending', 3, otherDigest, 'InProgress') + +const cases = [ + { + name: 'accepts the latest completed acknowledged release in unordered history', + histories: [ + [older, current], + [older, current], + ], + allowed: true, + }, + { + name: 'rejects a stale acknowledgement after a different release succeeds', + histories: [[execution('different', 3, otherDigest), current]], + allowed: false, + }, + { + name: 'rejects a newer pending release even when an older success appears first', + histories: [[current, pending]], + allowed: false, + }, + { + name: 'rejects a newer deployment discovered in the final unordered history', + histories: [[current], [current, pending]], + allowed: false, + }, +] as const + +/** Exercises the preflight CLI against deployment histories without contacting an AWS account. */ +describe('Project rollout release acknowledgement', () => { + it.each(cases)('$name', async ({ histories, allowed }) => { + const directory = await mkdtemp(join(tmpdir(), 'project-rollout-')) + try { + const fixture = join(directory, 'history.json') + await writeFile(fixture, JSON.stringify({ histories, digest })) + await writeFile( + join(directory, 'aws'), + `#!/usr/bin/env python3 +import json, os, pathlib, sys +fixture = pathlib.Path(os.environ['PROJECT_ROLLOUT_FIXTURE']) +data = json.loads(fixture.read_text()) +args = sys.argv +if 'list-pipeline-executions' in args: + counter = fixture.with_suffix('.count') + index = int(counter.read_text()) if counter.exists() else 0 + counter.write_text(str(index + 1)) + result = {'pipelineExecutionSummaries': data['histories'][min(index, len(data['histories']) - 1)]} +elif 'get-deployment-group' in args: + result = {'deploymentGroupInfo': {'ecsServices': [{'clusterName': 'fixture', 'serviceName': 'fixture'}]}} +elif 'describe-services' in args: + result = {'services': [{'desiredCount': 1, 'runningCount': 1, 'pendingCount': 0}]} +elif 'list-tasks' in args: + result = {'taskArns': ['fixture-task'] if args[args.index('--desired-status') + 1] == 'RUNNING' else []} +elif 'describe-tasks' in args: + result = {'tasks': [{'lastStatus': 'RUNNING', 'desiredStatus': 'RUNNING', 'containers': [{'name': 'app', 'imageDigest': data['digest']}]}]} +else: + raise SystemExit('Unexpected AWS command') +print(json.dumps(result)) +`, + { mode: 0o700 } + ) + const result = spawnSync( + 'python3', + [ + resolve('.github/scripts/check-project-rollout.py'), + '--environment', + 'staging', + '--region', + 'fixture-region', + '--expected-image-digest', + digest, + ], + { + encoding: 'utf8', + timeout: 10_000, + env: { + ...process.env, + PATH: `${directory}${delimiter}${process.env.PATH}`, + PROJECT_ROLLOUT_FIXTURE: fixture, + }, + } + ) + if (allowed) { + expect(result.status, result.stderr).toBe(0) + expect(JSON.parse(result.stdout)).toMatchObject({ + ecsRetired: true, + pipelineExecutionId: 'current', + }) + } else { + expect(result.status, result.stdout).toBe(1) + expect(result.stderr).toContain('Project rollout preflight refused:') + } + } finally { + await rm(directory, { recursive: true, force: true }) + } + }) +}) From d3c56a0c9273c38ac9e8c38aa6811b9149ddb31e Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sun, 4 Oct 2026 22:18:33 -0700 Subject: [PATCH 020/113] test(projects): detect missing lifecycle validation calls --- packages/db/scripts/project-contract.integration.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index ef8242daa87..2b1d4c67579 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -92,7 +92,8 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await tx`UPDATE workspace SET archived_at = now()` await tx`UPDATE project SET archived_at = now()` }) - const [calls] = await sql`SELECT last_value::int AS count FROM project_validation_count` + const [calls] = + await sql`SELECT CASE WHEN is_called THEN last_value::int ELSE 0 END AS count FROM project_validation_count` expect(calls.count).toBe(1) const [state] = await sql`SELECT count(*)::int AS count FROM workspace WHERE archived_at IS NOT NULL` From 8f36f2d20f0c4ad95935cd89be58eaef4e940f9e Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sun, 4 Oct 2026 22:33:11 -0700 Subject: [PATCH 021/113] test(projects): verify concurrent lifecycle lock contention --- .../scripts/project-contract.integration.ts | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 2b1d4c67579..2e4f73529e0 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -381,20 +381,35 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await release.promise }) await archived.promise - const started = createDeferred() + const started = createDeferred() const second = sql .begin(`isolation level ${isolation}`, async (tx) => { - await tx`SELECT count(*) FROM workspace WHERE archived_at IS NULL` - started.resolve() + const [connection] = + await tx`SELECT pg_backend_pid() AS pid, count(*) FROM workspace WHERE archived_at IS NULL` + started.resolve(connection.pid) await tx`UPDATE workspace SET archived_at = now() WHERE id = 'child'` }) .then( () => null, (error: unknown) => error ) - await started.promise - release.resolve() - await first + const secondPid = await started.promise + try { + let waiting = false + for (let attempt = 0; attempt < 100; attempt++) { + const [state] = + await sql`SELECT cardinality(pg_blocking_pids(${secondPid})) > 0 AS waiting` + if (state.waiting) { + waiting = true + break + } + await sleep(10) + } + expect(waiting).toBe(true) + } finally { + release.resolve() + await first + } const failure = await second expect(['23514', '40001', '40P01']).toContain(getPostgresErrorCode(failure)) expect(await sql`SELECT 1 FROM workspace WHERE archived_at IS NULL`).toHaveLength(1) From 25d72808b5c62d7aad39c8df90ed0471f61650ae Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sun, 4 Oct 2026 02:37:26 -0700 Subject: [PATCH 022/113] feat: establish durable file ownership and rollout compatibility Add canonical entity ownership while preserving legacy workspace writers. Make storage accounting, lifecycle cleanup, search, document artifacts, and realtime transport tolerate project-owned data before feature activation. Keep Project file user/API/tool entry points in the stacked feature change. --- .github/workflows/test-build.yml | 7 +- apps/realtime/src/access-revalidation.ts | 14 +- apps/realtime/src/handlers/file-doc-app.ts | 110 +- .../handlers/file-doc-editor.integration.ts | 223 + .../handlers/file-doc-project.integration.ts | 251 + apps/realtime/src/handlers/file-doc-store.ts | 151 +- apps/realtime/src/handlers/file-doc.ts | 392 +- apps/realtime/src/handlers/room-join-auth.ts | 19 +- apps/realtime/src/middleware/permissions.ts | 34 +- apps/realtime/src/routes/http.ts | 79 +- apps/realtime/vitest.config.ts | 57 +- apps/realtime/vitest.integration.setup.ts | 14 + apps/sim/app/api/files/authorization.ts | 15 +- .../public/[token]/content/route.test.ts | 2 +- .../api/files/public/[token]/content/route.ts | 2 +- .../api/files/serve/[...path]/route.test.ts | 2 +- .../app/api/files/serve/[...path]/route.ts | 4 +- apps/sim/app/api/files/uploads/finalizers.ts | 4 + .../api/folders/[id]/duplicate/route.test.ts | 2 + .../app/api/folders/[id]/duplicate/route.ts | 3 +- apps/sim/app/api/folders/[id]/route.test.ts | 2 + apps/sim/app/api/folders/[id]/route.ts | 5 +- apps/sim/app/api/folders/route.test.ts | 4 + .../api/v1/admin/folders/[id]/export/route.ts | 5 +- apps/sim/app/api/v2/files/uploads/utils.ts | 26 +- .../[uploadId]/parts/[partNumber]/route.ts | 4 + .../app/api/v2/uploads/[uploadId]/route.ts | 4 + apps/sim/background/cleanup-file-versions.ts | 13 +- .../background/cleanup-soft-deletes.test.ts | 9 +- apps/sim/background/cleanup-soft-deletes.ts | 16 +- .../lib/billing/cleanup-dispatcher.test.ts | 1 + apps/sim/lib/billing/cleanup-dispatcher.ts | 71 +- .../lib/billing/core/billing-attribution.ts | 12 +- .../lib/billing/core/limit-notifications.ts | 14 +- .../billing/organizations/lock-order.test.ts | 23 +- .../pause-pro-for-coverage.test.ts | 7 +- apps/sim/lib/billing/storage/context.ts | 72 +- apps/sim/lib/billing/storage/index.ts | 5 +- apps/sim/lib/billing/storage/limits.ts | 10 +- .../sim/lib/billing/storage/payer-transfer.ts | 437 +- .../storage/project-accounting.integration.ts | 591 + apps/sim/lib/billing/storage/tracking.ts | 116 +- apps/sim/lib/core/orchestration/types.ts | 4 + apps/sim/lib/core/outbox/processor.test.ts | 3 + apps/sim/lib/core/outbox/processor.ts | 4 + .../payloads/file-secret-provenance.ts | 1 + apps/sim/lib/folders/naming.ts | 21 +- apps/sim/lib/folders/queries.test.ts | 2 + apps/sim/lib/folders/queries.ts | 17 +- apps/sim/lib/folders/scope.ts | 40 + apps/sim/lib/folders/subtree.ts | 19 + apps/sim/lib/knowledge/documents/service.ts | 4 +- .../tools/server/files/doc-compile-error.ts | 25 - .../tools/server/files/edit-content.ts | 2 +- .../server/files/file-edit-roundtrip.test.ts | 2 +- .../tools/server/files/workspace-file.ts | 14 +- apps/sim/lib/projects/account-deletion.ts | 67 +- .../private-purge.integration.ts | 586 + .../__integration__/retention.integration.ts | 514 + .../files/application/document-lifecycle.ts | 32 + apps/sim/lib/projects/files/prefix-cleanup.ts | 315 + apps/sim/lib/projects/files/purge.ts | 90 + apps/sim/lib/projects/files/retention.ts | 325 + apps/sim/lib/projects/membership.ts | 34 +- apps/sim/lib/public-shares/share-manager.ts | 164 +- apps/sim/lib/realtime/notify.ts | 28 +- apps/sim/lib/uploads/config.ts | 3 + .../workspace-file-folder-manager.test.ts | 7 + .../workspace-file-folder-manager.ts | 1467 +- .../workspace-file-live-doc-outbox.ts | 80 +- .../workspace/workspace-file-manager.ts | 833 +- .../workspace-file-secret-provenance.ts | 17 +- .../workspace-file-storage-accounting.test.ts | 116 +- .../workspace-file-storage-cleanup-outbox.ts | 21 +- .../workspace/workspace-file-versions.ts | 220 +- apps/sim/lib/uploads/core/storage-client.ts | 6 +- .../lib/uploads/documents/compile-error.ts | 11 + .../documents/compile.test.ts} | 4 +- .../documents/compile.ts} | 159 +- .../documents/compiled-store.test.ts} | 2 +- .../documents/compiled-store.ts} | 57 +- apps/sim/lib/uploads/documents/index.ts | 6 + .../lib/uploads/documents/input-identity.ts | 21 + .../files => uploads/documents}/pptx-shim.ts | 0 apps/sim/lib/uploads/documents/references.ts | 21 + apps/sim/lib/uploads/documents/render.ts | 35 + .../documents/servable.test.ts} | 18 +- .../providers/google-cloud-storage/client.ts | 7 +- apps/sim/lib/uploads/shared/types.ts | 1 + .../lib/uploads/upload-session/presenter.ts | 15 + .../registration-lifetime.integration.ts | 154 + .../sim/lib/uploads/upload-session/service.ts | 5 + apps/sim/lib/uploads/upload-session/types.ts | 4 + apps/sim/lib/uploads/utils/doc-not-ready.ts | 2 +- .../uploads/utils/file-utils.server.test.ts | 2 +- .../lib/uploads/utils/file-utils.server.ts | 5 +- apps/sim/lib/users/account-deletion.ts | 10 +- .../fetch-servable-workspace-file-buffer.ts | 28 +- .../application/list-workspace-files.ts | 26 +- .../resolve-rendered-workspace-artifact.ts | 30 +- .../search-workspace-file-content.test.ts | 3 +- .../search-workspace-file-content.ts | 51 +- .../sim/lib/workspace-files/owner-adapters.ts | 24 + .../lib/workspace-files/ownership-query.ts | 48 + .../sim/lib/workspace-files/ownership.test.ts | 111 + apps/sim/lib/workspace-files/ownership.ts | 76 + apps/sim/lib/workspace-files/search/README.md | 8 + .../workspace-files/search/artifact-ready.ts | 95 + .../lib/workspace-files/search/candidates.ts | 36 +- .../search/chunks.integration.ts | 11 +- .../lib/workspace-files/search/constants.ts | 2 + .../lib/workspace-files/search/delivery.ts | 150 + .../search/dispatcher.integration.ts | 117 +- .../workspace-files/search/dispatcher.test.ts | 24 +- .../lib/workspace-files/search/dispatcher.ts | 306 +- .../workspace-files/search/extract.test.ts | 97 +- .../sim/lib/workspace-files/search/extract.ts | 45 +- .../lib/workspace-files/search/index-state.ts | 121 +- .../workspace-files/search/indexing.test.ts | 68 +- .../lib/workspace-files/search/indexing.ts | 60 +- .../workspace-files/search/owner-policy.ts | 52 + .../search/owners.integration.ts | 824 + .../lib/workspace-files/search/repository.ts | 128 +- apps/sim/lib/workspace-files/search/scope.ts | 76 + apps/sim/lib/workspace-files/search/source.ts | 111 + apps/sim/lib/workspaces/admin-move.test.ts | 54 +- apps/sim/lib/workspaces/admin-move.ts | 29 +- .../organization-workspaces.test.ts | 66 +- .../lib/workspaces/organization-workspaces.ts | 17 +- apps/sim/lib/workspaces/utils.test.ts | 13 +- apps/sim/lib/workspaces/utils.ts | 110 +- apps/sim/sandbox-tasks/pptx-generate.ts | 2 +- knip.jsonc | 10 +- packages/auth/src/principal.ts | 2 + .../db/file-creator-lifetime.integration.ts | 349 + .../db/file-entity-ownership.integration.ts | 362 + ...le-folder-version-ownership.integration.ts | 643 + .../migrations/0396_file_entity_ownership.sql | 144 + .../0397_file_folder_version_ownership.sql | 297 + .../migrations/0398_file_creator_lifetime.sql | 75 + .../0399_project_file_upload_sessions.sql | 1 + .../0400_public_share_entity_ownership.sql | 104 + .../0401_file_search_owner_scope.sql | 168 + .../db/migrations/meta/0396_snapshot.json | 29987 +++++++++++++++ .../db/migrations/meta/0397_snapshot.json | 30105 +++++++++++++++ .../db/migrations/meta/0398_snapshot.json | 30125 +++++++++++++++ .../db/migrations/meta/0399_snapshot.json | 30126 +++++++++++++++ .../db/migrations/meta/0400_snapshot.json | 30170 +++++++++++++++ .../db/migrations/meta/0401_snapshot.json | 30535 ++++++++++++++++ packages/db/migrations/meta/_journal.json | 42 + packages/db/package.json | 4 + .../db/public-share-ownership.integration.ts | 375 + packages/db/schema.ts | 209 +- .../0003_backfill_workspace_storage_usage.ts | 66 +- .../0030_backfill_workspace_file_entities.ts | 88 + .../0031_backfill_file_folder_entities.ts | 80 + .../0032_backfill_project_file_creators.ts | 49 + .../0033_backfill_public_share_entities.ts | 82 + packages/db/script-migrations/index.ts | 8 + packages/platform-authz/src/room-policy.ts | 1 + packages/realtime-protocol/src/file-doc.ts | 19 + packages/realtime-protocol/src/rooms.ts | 17 + .../src/mocks/billing-payer-transfer.mock.ts | 19 + .../src/mocks/project-membership.mock.ts | 6 + .../src/mocks/schema-tables.generated.ts | 26 + .../src/mocks/workspace-file-folders.mock.ts | 2 + scripts/check-unused-exports.baseline.json | 9 - scripts/test-integration.ts | 8 +- 168 files changed, 193448 insertions(+), 2338 deletions(-) create mode 100644 apps/realtime/src/handlers/file-doc-editor.integration.ts create mode 100644 apps/realtime/src/handlers/file-doc-project.integration.ts create mode 100644 apps/realtime/vitest.integration.setup.ts create mode 100644 apps/sim/lib/billing/storage/project-accounting.integration.ts delete mode 100644 apps/sim/lib/mothership/tools/server/files/doc-compile-error.ts create mode 100644 apps/sim/lib/projects/files/__integration__/private-purge.integration.ts create mode 100644 apps/sim/lib/projects/files/__integration__/retention.integration.ts create mode 100644 apps/sim/lib/projects/files/application/document-lifecycle.ts create mode 100644 apps/sim/lib/projects/files/prefix-cleanup.ts create mode 100644 apps/sim/lib/projects/files/purge.ts create mode 100644 apps/sim/lib/projects/files/retention.ts create mode 100644 apps/sim/lib/uploads/documents/compile-error.ts rename apps/sim/lib/{mothership/tools/server/files/doc-compile.test.ts => uploads/documents/compile.test.ts} (98%) rename apps/sim/lib/{mothership/tools/server/files/doc-compile.ts => uploads/documents/compile.ts} (92%) rename apps/sim/lib/{mothership/tools/server/files/doc-compiled-store.test.ts => uploads/documents/compiled-store.test.ts} (99%) rename apps/sim/lib/{mothership/tools/server/files/doc-compiled-store.ts => uploads/documents/compiled-store.ts} (78%) create mode 100644 apps/sim/lib/uploads/documents/index.ts create mode 100644 apps/sim/lib/uploads/documents/input-identity.ts rename apps/sim/lib/{mothership/tools/server/files => uploads/documents}/pptx-shim.ts (100%) create mode 100644 apps/sim/lib/uploads/documents/references.ts create mode 100644 apps/sim/lib/uploads/documents/render.ts rename apps/sim/lib/{mothership/tools/server/files/doc-servable.test.ts => uploads/documents/servable.test.ts} (97%) create mode 100644 apps/sim/lib/uploads/upload-session/presenter.ts create mode 100644 apps/sim/lib/uploads/upload-session/registration-lifetime.integration.ts create mode 100644 apps/sim/lib/workspace-files/owner-adapters.ts create mode 100644 apps/sim/lib/workspace-files/ownership-query.ts create mode 100644 apps/sim/lib/workspace-files/ownership.test.ts create mode 100644 apps/sim/lib/workspace-files/ownership.ts create mode 100644 apps/sim/lib/workspace-files/search/artifact-ready.ts create mode 100644 apps/sim/lib/workspace-files/search/delivery.ts create mode 100644 apps/sim/lib/workspace-files/search/owner-policy.ts create mode 100644 apps/sim/lib/workspace-files/search/owners.integration.ts create mode 100644 apps/sim/lib/workspace-files/search/scope.ts create mode 100644 apps/sim/lib/workspace-files/search/source.ts create mode 100644 packages/db/file-creator-lifetime.integration.ts create mode 100644 packages/db/file-entity-ownership.integration.ts create mode 100644 packages/db/file-folder-version-ownership.integration.ts create mode 100644 packages/db/migrations/0396_file_entity_ownership.sql create mode 100644 packages/db/migrations/0397_file_folder_version_ownership.sql create mode 100644 packages/db/migrations/0398_file_creator_lifetime.sql create mode 100644 packages/db/migrations/0399_project_file_upload_sessions.sql create mode 100644 packages/db/migrations/0400_public_share_entity_ownership.sql create mode 100644 packages/db/migrations/0401_file_search_owner_scope.sql create mode 100644 packages/db/migrations/meta/0396_snapshot.json create mode 100644 packages/db/migrations/meta/0397_snapshot.json create mode 100644 packages/db/migrations/meta/0398_snapshot.json create mode 100644 packages/db/migrations/meta/0399_snapshot.json create mode 100644 packages/db/migrations/meta/0400_snapshot.json create mode 100644 packages/db/migrations/meta/0401_snapshot.json create mode 100644 packages/db/public-share-ownership.integration.ts create mode 100644 packages/db/script-migrations/0030_backfill_workspace_file_entities.ts create mode 100644 packages/db/script-migrations/0031_backfill_file_folder_entities.ts create mode 100644 packages/db/script-migrations/0032_backfill_project_file_creators.ts create mode 100644 packages/db/script-migrations/0033_backfill_public_share_entities.ts create mode 100644 packages/testing/src/mocks/billing-payer-transfer.mock.ts create mode 100644 packages/testing/src/mocks/project-membership.mock.ts diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index e6ef55b6ae2..f94dd9a446a 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -10,7 +10,7 @@ permissions: jobs: postgres-integration: # Runs the real-infrastructure test layer: every `*.integration.ts` in packages/db and - # apps/sim, discovered by glob (`vitest run --mode integration`), against the database each + # apps/sim and apps/realtime, discovered by glob (`vitest run --mode integration`), against the database each # provisioning path produces. A new integration suite needs no workflow change. name: PostgreSQL integration (${{ matrix.provision }}) runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-latest' }} @@ -94,6 +94,10 @@ jobs: TZ: America/Los_Angeles run: bun run test --mode integration + - name: Run apps/realtime integration tests + working-directory: apps/realtime + run: bun run test --mode integration + - name: Verify cumulative billing timeout recovery on PostgreSQL 16 if: matrix.provision == 'push' working-directory: apps/sim @@ -111,6 +115,7 @@ jobs: path: | packages/db/test-results/*.json apps/sim/test-results/*.json + apps/realtime/test-results/*.json if-no-files-found: warn retention-days: 14 diff --git a/apps/realtime/src/access-revalidation.ts b/apps/realtime/src/access-revalidation.ts index 8e0ca514812..29a03db6272 100644 --- a/apps/realtime/src/access-revalidation.ts +++ b/apps/realtime/src/access-revalidation.ts @@ -1,8 +1,10 @@ import { createLogger } from '@sim/logger' import { ROOM_MEMBERSHIP_ACTIONS, satisfiesRoomMembership } from '@sim/platform-authz/room-policy' import type { AccessRevokedBroadcast } from '@sim/realtime-protocol/events' +import { FILE_DOC_EVENTS, type FileDocPermission } from '@sim/realtime-protocol/file-doc' import { parseRoomName, + projectFileDocTarget, ROOM_TYPES, type RoomRef, type RoomType, @@ -327,11 +329,21 @@ export function startAccessRevalidationSweep(roomManager: IRoomManager): AccessR // resolution keeps running in the background and is re-raced when the // rotation returns to this socket, so it is acted on once it settles. const role = await Promise.race([ - resolveCurrentRoomPermission(userId, room, fallbackRoleFor(room.type)), + room.type === ROOM_TYPES.PROJECT_FILE_DOC + ? resolveCurrentRoomPermission(userId, room, fallbackRoleFor(room.type), socket.id) + : resolveCurrentRoomPermission(userId, room, fallbackRoleFor(room.type)), sleep(Math.min(SCAN_SOCKET_TIMEOUT_MS, remainingBudget)).then(() => SCAN_TIMED_OUT), ]) // {@link SCAN_TIMED_OUT} is the only symbol this race can yield; matching on // the type narrows it out of the permission comparison below. + if (typeof role !== 'symbol' && room.type === ROOM_TYPES.PROJECT_FILE_DOC) { + const target = projectFileDocTarget(room) + if (target) + socket.emit(FILE_DOC_EVENTS.PERMISSION, { + ...target, + canWrite: role === 'write' || role === 'admin', + } satisfies FileDocPermission) + } if (typeof role === 'symbol') { logger.warn( `Authorization check timed out for user ${userId} on ${name}; skipping this pass` diff --git a/apps/realtime/src/handlers/file-doc-app.ts b/apps/realtime/src/handlers/file-doc-app.ts index 072d94f20d6..85372d7cafa 100644 --- a/apps/realtime/src/handlers/file-doc-app.ts +++ b/apps/realtime/src/handlers/file-doc-app.ts @@ -1,4 +1,5 @@ -import { FILE_DOC_TIMEOUTS } from '@sim/realtime-protocol/file-doc' +import type { RoomAuthorizationResult } from '@sim/platform-authz/rooms' +import { FILE_DOC_INTERNAL_HEADERS, FILE_DOC_TIMEOUTS } from '@sim/realtime-protocol/file-doc' import { env, getBaseUrl } from '@/env' /** @@ -8,10 +9,24 @@ import { env, getBaseUrl } from '@/env' * timeouts (and their ordering vs. the app-side bounds) live in the shared `FILE_DOC_TIMEOUTS`. */ -function postToApp(path: string, payload: unknown, timeoutMs: number): Promise { +function postToApp( + path: string, + payload: unknown, + timeoutMs: number, + actor?: { userId: string; connectionId: string } +): Promise { return fetch(`${getBaseUrl()}${path}`, { method: 'POST', - headers: { 'Content-Type': 'application/json', 'x-api-key': env.INTERNAL_API_SECRET }, + headers: { + 'Content-Type': 'application/json', + 'x-api-key': env.INTERNAL_API_SECRET, + ...(actor + ? { + [FILE_DOC_INTERNAL_HEADERS.userId]: actor.userId, + [FILE_DOC_INTERNAL_HEADERS.connectionId]: actor.connectionId, + } + : {}), + }, body: JSON.stringify(payload), signal: AbortSignal.timeout(timeoutMs), }) @@ -123,3 +138,92 @@ export async function fetchFileDocPersist( } return body } + +interface ProjectDocumentRequest { + projectId: string + fileId: string + userId: string + connectionId: string +} + +function projectDocumentPath( + target: ProjectDocumentRequest, + action: 'access' | 'seed' | 'persist' +) { + return `/api/internal/project-file-doc/${encodeURIComponent(target.projectId)}/${encodeURIComponent(target.fileId)}/${action}` +} + +/** Resolve current Project membership without manufacturing a workspace permission or identity. */ +export async function fetchProjectFileDocAccess( + target: ProjectDocumentRequest +): Promise { + const response = await postToApp( + projectDocumentPath(target, 'access'), + {}, + FILE_DOC_TIMEOUTS.seedRequestMs, + target + ) + if (response.status === 403 || response.status === 404) { + return { allowed: false, status: response.status, workspaceId: null, workspacePermission: null } + } + if (!response.ok) throw new Error(`Project document authorization failed: ${response.status}`) + const body = (await response.json()) as { + projectId?: unknown + fileId?: unknown + canRead?: unknown + canWrite?: unknown + docId?: unknown + } + if ( + body.projectId !== target.projectId || + body.fileId !== target.fileId || + body.canRead !== true || + typeof body.canWrite !== 'boolean' || + (body.docId !== null && + (typeof body.docId !== 'string' || !body.docId || body.docId.length > 128)) + ) + throw new Error('Malformed Project document authorization') + return { + allowed: true, + status: 200, + workspaceId: null, + workspacePermission: body.canWrite ? 'write' : 'read', + docId: body.docId, + } +} + +/** Fetch a seed under the joining socket's current Project read access. */ +export async function fetchProjectFileDocSeed( + target: ProjectDocumentRequest +): Promise<{ update: Uint8Array; version: number }> { + const response = await postToApp( + projectDocumentPath(target, 'seed'), + {}, + FILE_DOC_TIMEOUTS.seedRequestMs, + target + ) + if (!response.ok) throw new Error(`Project document seed failed: ${response.status}`) + const body = (await response.json()) as { update?: unknown; version?: unknown } + if (typeof body.update !== 'string' || typeof body.version !== 'number') + throw new Error('Malformed Project document seed') + return { update: new Uint8Array(Buffer.from(body.update, 'base64')), version: body.version } +} + +/** Persist as the authenticated author captured with the accepted stream snapshot. */ +export async function fetchProjectFileDocPersist( + target: ProjectDocumentRequest, + docState: Uint8Array, + expectedVersion?: number +): Promise { + const response = await postToApp( + projectDocumentPath(target, 'persist'), + { docState: Buffer.from(docState).toString('base64'), expectedVersion }, + FILE_DOC_TIMEOUTS.persistRequestMs, + target + ) + if (!response.ok) throw new Error(`Project document persist failed: ${response.status}`) + const body = (await response.json()) as PersistResult + if (!['persisted', 'missing', 'conflict', 'deferred'].includes(body?.status)) + throw new Error('Malformed Project document persist') + return body +} diff --git a/apps/realtime/src/handlers/file-doc-editor.integration.ts b/apps/realtime/src/handlers/file-doc-editor.integration.ts new file mode 100644 index 00000000000..efe7759ec92 --- /dev/null +++ b/apps/realtime/src/handlers/file-doc-editor.integration.ts @@ -0,0 +1,223 @@ +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname, resolve } from 'node:path' +import { FILE_DOC_SEED } from '@sim/realtime-protocol/file-doc' +import { getErrorMessage } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { createClient } from 'redis' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import * as Y from 'yjs' +import { FileDocStore } from '@/handlers/file-doc-store' + +const redisUrl = process.env.REDIS_URL +if (!redisUrl) throw new Error('Integration setup must provide disposable Redis') +const redis = createClient({ url: redisUrl }) +const store = new FileDocStore(redisUrl) +const rooms: { name: string; doc: Y.Doc }[] = [] +const checks: { name: string; status: string; durationMs: number; error?: string }[] = [] +const alice = { userId: 'fixture-alice', connectionId: 'socket-alice' } +const bob = { userId: 'fixture-bob', connectionId: 'socket-bob' } + +beforeAll(async () => { + await redis.connect() + await store.init() +}) + +async function fixture() { + const name = `project-file-doc:${generateId()}/${generateId()}` + const doc = new Y.Doc() + const generation = generateId() + doc.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.flag, true) + doc.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.docIdKey, generation) + await store.seedIfEmpty(name, Y.encodeStateAsUpdate(doc), 1) + await store.attachRoom(name, doc) + rooms.push({ name, doc }) + return { name, doc, generation } +} + +function update(doc: Y.Doc, value: string) { + const before = Y.encodeStateVector(doc) + doc.getText('body').insert(doc.getText('body').length, value) + return Y.encodeStateAsUpdate(doc, before) +} + +function check(name: string, run: () => Promise) { + it(name, async () => { + const start = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - start }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - start, + error: getErrorMessage(error), + }) + throw error + } + }) +} + +describe('Project document editor attribution over real Redis', () => { + check( + 'deduplication retains the accepted author and obsolete generations cannot append', + async () => { + const f = await fixture() + const delta = update(f.doc, 'A') + await store.publishClientUpdateAndWait(f.name, 'update-a', delta, f.generation, alice) + await store.publishClientUpdateAndWait(f.name, 'update-a', delta, f.generation, bob) + const state = await store.getStreamSnapshot(f.name, f.generation) + expect(state?.editor).toEqual(alice) + await expect( + store.publishClientUpdateAndWait(f.name, 'old', delta, 'obsolete', bob) + ).rejects.toThrow() + expect((await store.getStreamSnapshot(f.name, f.generation))?.editor).toEqual(alice) + } + ) + + check( + 'Project updates without an authenticated editor are rejected before acceptance', + async () => { + const f = await fixture() + await expect( + store.publishClientUpdateAndWait(f.name, 'unknown', update(f.doc, 'X'), f.generation) + ).rejects.toThrow('Project document updates require an authenticated editor') + expect(await redis.xLen(`filedoc:stream:${f.name}`)).toBe(1) + } + ) + + check( + 'real compaction preserves the editor for a headless replica reconstructing accepted bytes', + async () => { + const f = await fixture() + for (let index = 0; index < 447; index++) { + await store.publishClientUpdateAndWait( + f.name, + `edit-${index}`, + update(f.doc, 'x'), + f.generation, + index === 446 ? bob : alice + ) + await store.catchUp(f.name) + } + store.publish(f.name, update(f.doc, 'x'), true) + await expect + .poll(async () => + (await redis.xRange(`filedoc:stream:${f.name}`, '-', '+')).some( + (entry) => entry.message.c === '1' + ) + ) + .toBe(true) + store.detachRoom(f.name) + const state = await store.getStreamSnapshot(f.name, f.generation) + expect(state?.editor).toEqual(bob) + const restored = new Y.Doc() + try { + if (!state) throw new Error('Missing reconstructed state') + Y.applyUpdate(restored, state.docState) + expect(restored.getText('body').toString()).toBe('x'.repeat(448)) + } finally { + restored.destroy() + } + } + ) + + check( + 'retiring a generation fences old seeds and delayed retirement preserves the replacement', + async () => { + const f = await fixture() + await store.publishClientUpdateAndWait( + f.name, + 'before-archive', + update(f.doc, 'old'), + f.generation, + alice + ) + const replacement = new Y.Doc() + const replacementId = generateId() + replacement.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.flag, true) + replacement.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.docIdKey, replacementId) + try { + expect(await store.retireDocumentGeneration(f.name, f.generation, replacementId)).toEqual({ + status: 'applied', + docId: f.generation, + }) + expect(await store.seedIfEmpty(f.name, Y.encodeStateAsUpdate(f.doc), 1, true)).toBe(false) + await expect( + store.publishClientUpdateAndWait(f.name, 'stale', update(f.doc, '!'), f.generation, alice) + ).rejects.toThrow() + expect(await store.seedIfEmpty(f.name, Y.encodeStateAsUpdate(replacement), 1, true)).toBe( + true + ) + await store.publishClientUpdateAndWait( + f.name, + 'after-restore', + update(replacement, 'new'), + replacementId, + bob + ) + expect(await store.retireDocumentGeneration(f.name, f.generation, replacementId)).toEqual({ + status: 'stale', + }) + const state = await store.getStreamSnapshot(f.name, replacementId) + expect(state?.editor).toEqual(bob) + const restored = new Y.Doc() + try { + if (!state) throw new Error('Replacement generation was lost') + Y.applyUpdate(restored, state.docState) + expect(restored.getText('body').toString()).toBe('new') + } finally { + restored.destroy() + } + } finally { + replacement.destroy() + } + } + ) + + check( + 'a late prefix snapshot cannot replace a newer editor and unknown newer edits erase attribution', + async () => { + const f = await fixture() + await store.publishClientUpdateAndWait( + f.name, + 'alice', + update(f.doc, 'A'), + f.generation, + alice + ) + const prefix = Y.encodeStateAsUpdate(f.doc) + const [entry] = await redis.xRevRange(`filedoc:stream:${f.name}`, '+', '-', { COUNT: 1 }) + if (!entry) throw new Error('Missing accepted prefix') + await store.publishClientUpdateAndWait(f.name, 'bob', update(f.doc, 'B'), f.generation, bob) + await redis.xAdd(`filedoc:stream:${f.name}`, '*', { + u: Buffer.from(prefix).toString('base64'), + s: '1', + c: '1', + g: f.generation, + eu: alice.userId, + ec: alice.connectionId, + ei: entry.id, + }) + expect((await store.getStreamSnapshot(f.name, f.generation))?.editor).toEqual(bob) + await redis.xAdd(`filedoc:stream:${f.name}`, '*', { + u: Buffer.from(update(f.doc, '?')).toString('base64'), + }) + expect((await store.getStreamSnapshot(f.name, f.generation))?.editor).toBeNull() + } + ) +}) + +afterAll(async () => { + await store.shutdown() + for (const { name, doc } of rooms) { + const keys = await redis.keys(`filedoc:*:${name}`) + if (keys.length) await redis.del(keys) + doc.destroy() + } + await redis.quit() + const reportPath = + process.env.PROJECT_FILE_REDIS_REPORT_PATH ?? resolve('test-results/project-file-redis.json') + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) +}) diff --git a/apps/realtime/src/handlers/file-doc-project.integration.ts b/apps/realtime/src/handlers/file-doc-project.integration.ts new file mode 100644 index 00000000000..2d02c19a335 --- /dev/null +++ b/apps/realtime/src/handlers/file-doc-project.integration.ts @@ -0,0 +1,251 @@ +import { mkdir, writeFile } from 'node:fs/promises' +import { createServer, type Server as HttpServer } from 'node:http' +import { dirname, resolve } from 'node:path' +import { + FILE_DOC_EVENTS, + FILE_DOC_SCHEMA_VERSION, + FILE_DOC_SEED, + type JoinFileDocSuccess, +} from '@sim/realtime-protocol/file-doc' +import { getErrorMessage } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { Server } from 'socket.io' +import { io as connect, type Socket } from 'socket.io-client' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import * as Y from 'yjs' +import { env } from '@/env' +import { + cleanupFileDocForSocket, + flushAllFileDocRooms, + setupWorkspaceFileDocHandlers, +} from '@/handlers/file-doc' +import type { AuthenticatedSocket } from '@/middleware/auth' +import { beginRoomPermissionRead, commitRoomPermission } from '@/middleware/permissions' +import { MemoryRoomManager } from '@/rooms' + +const projectId = generateId() +const fileId = generateId() +let docId = generateId() +const document = new Y.Doc() +document.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.flag, true) +document.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.docIdKey, docId) +let seed = Buffer.from(Y.encodeStateAsUpdate(document)).toString('base64') +const clients: Socket[] = [] +const actors = new Map([ + ['reader', 'read'], + ['writer', 'write'], +]) +const checks: { name: string; status: string; durationMs: number; error?: string }[] = [] +let app: HttpServer +let http: HttpServer +let io: Server +let socketUrl: string +const capturedActors: string[] = [] + +async function listen(server: HttpServer) { + await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)) + const address = server.address() + if (!address || typeof address === 'string') throw new Error('Missing fixture port') + return `http://127.0.0.1:${address.port}` +} + +beforeAll(async () => { + env.REDIS_URL = undefined + app = createServer(async (request, response) => { + const actor = request.headers['x-sim-subject-user-id'] + const connection = request.headers['x-sim-realtime-connection-id'] + response.setHeader('Content-Type', 'application/json') + if ( + request.headers['x-api-key'] !== env.INTERNAL_API_SECRET || + typeof actor !== 'string' || + typeof connection !== 'string' || + !connection + ) { + response.writeHead(401).end('{}') + return + } + if (!request.url?.startsWith(`/api/internal/project-file-doc/${projectId}/${fileId}/`)) { + response.writeHead(404).end('{}') + return + } + const role = actors.get(actor) + if (!role) { + response.writeHead(404).end('{}') + return + } + if (request.url.endsWith('/access')) { + response.end( + JSON.stringify({ projectId, fileId, canRead: true, canWrite: role === 'write', docId }) + ) + return + } + if (request.url.endsWith('/seed')) { + response.end(JSON.stringify({ update: seed, version: 1 })) + return + } + if (request.url.endsWith('/persist')) { + if (role !== 'write') { + response.writeHead(403).end('{}') + return + } + capturedActors.push(actor) + response.end(JSON.stringify({ status: 'persisted', version: 2 })) + return + } + response.writeHead(404).end('{}') + }) + env.NEXT_PUBLIC_APP_URL = await listen(app) + http = createServer() + io = new Server(http) + const manager = new MemoryRoomManager(io) + io.on('connection', (socket) => { + const authed = socket as AuthenticatedSocket + authed.userId = socket.handshake.auth.actor + authed.userName = 'Fixture editor' + authed.userImage = 'https://fixture.invalid/avatar.png' + setupWorkspaceFileDocHandlers(authed, manager) + socket.on('disconnect', () => cleanupFileDocForSocket(socket.id, io)) + }) + socketUrl = await listen(http) +}) + +async function join(actor: string, assertedProject = projectId) { + const socket = connect(socketUrl, { transports: ['websocket'], auth: { actor } }) + clients.push(socket) + await new Promise((resolve) => socket.once('connect', resolve)) + const joined = new Promise((resolve, reject) => { + socket.once(FILE_DOC_EVENTS.JOIN_SUCCESS, resolve) + socket.once(FILE_DOC_EVENTS.JOIN_ERROR, (error) => reject(new Error(error.code))) + }) + const client = new Y.Doc() + const clientId = client.clientID + client.destroy() + socket.emit(FILE_DOC_EVENTS.JOIN, { + fileId, + projectId: assertedProject, + clientId, + schemaVersion: FILE_DOC_SCHEMA_VERSION, + }) + return { socket, joined: await joined } +} + +function check(name: string, run: () => Promise) { + it(name, async () => { + const start = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - start }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - start, + error: getErrorMessage(error), + }) + throw error + } + }) +} + +describe('Project documents across the Socket.IO and internal HTTP boundary', () => { + check('readers subscribe but cannot submit edits or become the persistence actor', async () => { + const reader = await join('reader') + expect(reader.joined.canWrite).toBe(false) + const write = new Y.Doc() + write.getText('body').insert(0, 'forged') + const reply = await reader.socket.timeout(1500).emitWithAck(FILE_DOC_EVENTS.UPDATE, { + fileId, + projectId, + docId, + updateId: generateId(), + update: Y.encodeStateAsUpdate(write), + }) + expect(reply).toMatchObject({ status: 'rejected', code: 'ACCESS_REVOKED' }) + expect( + io.sockets.sockets + .get(reader.socket.id ?? '') + ?.rooms.has(`project-file-doc:${projectId}/${fileId}`) + ).toBe(true) + await flushAllFileDocRooms() + expect(capturedActors).toEqual([]) + write.destroy() + }) + + check( + 'a writer can publish and a reader joining later never replaces the actual editor', + async () => { + const writer = await join('writer') + const edit = new Y.Doc() + Y.applyUpdate(edit, Buffer.from(seed, 'base64')) + edit.getText('body').insert(0, 'accepted') + const reply = await writer.socket.timeout(1500).emitWithAck(FILE_DOC_EVENTS.UPDATE, { + fileId, + projectId, + docId, + updateId: generateId(), + update: Y.encodeStateAsUpdate(edit), + }) + expect(reply).toMatchObject({ status: 'accepted' }) + await join('reader') + await flushAllFileDocRooms() + expect(capturedActors.at(-1)).toBe('writer') + edit.destroy() + } + ) + + check('a downgraded writer remains a reader but cannot submit another update', async () => { + const writer = await join('writer') + actors.set('writer', 'read') + commitRoomPermission( + 'writer', + { type: 'project-file-doc', id: `${projectId}/${fileId}` }, + 'read', + beginRoomPermissionRead() + ) + const reply = await writer.socket.timeout(1500).emitWithAck(FILE_DOC_EVENTS.UPDATE, { + fileId, + projectId, + docId, + updateId: generateId(), + update: Y.encodeStateAsUpdate(document), + }) + expect(reply).toMatchObject({ status: 'rejected', code: 'ACCESS_REVOKED' }) + expect( + io.sockets.sockets + .get(writer.socket.id ?? '') + ?.rooms.has(`project-file-doc:${projectId}/${fileId}`) + ).toBe(true) + }) + + check('another Project cannot address the same file through a room hint', async () => { + expect((await join('reader')).joined.canWrite).toBe(false) + await expect(join('reader', generateId())).rejects.toThrow('NOT_FOUND') + }) + check( + 'a restored canonical identity cannot join the prior live document before outbox delivery', + async () => { + const old = await join('reader') + const replacement = new Y.Doc() + docId = generateId() + replacement.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.flag, true) + replacement.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.docIdKey, docId) + seed = Buffer.from(Y.encodeStateAsUpdate(replacement)).toString('base64') + replacement.destroy() + expect(old.joined.docId).not.toBe(docId) + const restored = await join('reader') + expect(restored.joined.docId).toBe(docId) + } + ) +}) + +afterAll(async () => { + for (const socket of clients) socket.disconnect() + await flushAllFileDocRooms() + await new Promise((resolve) => io.close(() => resolve())) + await new Promise((resolve) => app.close(() => resolve())) + document.destroy() + const reportPath = + process.env.PROJECT_FILE_SOCKET_REPORT_PATH ?? resolve('test-results/project-file-socket.json') + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) +}) diff --git a/apps/realtime/src/handlers/file-doc-store.ts b/apps/realtime/src/handlers/file-doc-store.ts index 3fa761671ed..b8fe6e8f3b0 100644 --- a/apps/realtime/src/handlers/file-doc-store.ts +++ b/apps/realtime/src/handlers/file-doc-store.ts @@ -63,7 +63,11 @@ const RELEASE_LOCK_SCRIPT = * Returns 1 if THIS call wrote the seed, 0 if the stream already had content. */ const SEED_IF_EMPTY_SCRIPT = - "local version = redis.call('get', KEYS[3]); if version and tonumber(version) > tonumber(ARGV[6]) then return 0 end; if redis.call('xlen', KEYS[1]) == 0 then redis.call('set', KEYS[2], ARGV[3], 'EX', ARGV[4]); if ARGV[6] ~= '0' then redis.call('set', KEYS[3], ARGV[6], 'EX', ARGV[4]) end; redis.call('xadd', KEYS[1], '*', ARGV[1], ARGV[2], ARGV[5], ARGV[3]); redis.call('expire', KEYS[1], ARGV[4]); redis.call('expire', KEYS[4], ARGV[4]); return 1 else return 0 end" + "local generation = redis.call('get', KEYS[2]); if ARGV[7] == '1' and generation and generation ~= ARGV[8] and generation ~= ARGV[3] then return 0 end; local version = redis.call('get', KEYS[3]); if version and tonumber(version) > tonumber(ARGV[6]) then return 0 end; if redis.call('xlen', KEYS[1]) == 0 then redis.call('set', KEYS[2], ARGV[3], 'EX', ARGV[4]); if ARGV[6] ~= '0' then redis.call('set', KEYS[3], ARGV[6], 'EX', ARGV[4]) end; redis.call('xadd', KEYS[1], '*', ARGV[1], ARGV[2], ARGV[5], ARGV[3]); redis.call('expire', KEYS[1], ARGV[4]); redis.call('expire', KEYS[4], ARGV[4]); return 1 else return 0 end" + +/** Preserve a replacement identity even before its seed exists, fencing late seeds for the retired one. */ +const RETIRE_DOCUMENT_SCRIPT = + "local generation = redis.call('get', KEYS[2]); if generation ~= ARGV[1] then return false end; redis.call('set', KEYS[2], ARGV[2], 'EX', ARGV[3]); redis.call('del', KEYS[1], KEYS[4], KEYS[5]); redis.call('expire', KEYS[3], ARGV[3]); redis.call('expire', KEYS[6], ARGV[3]); return generation" /** Orders a durable replacement with seeds and merges, and fences publishers in the same transaction. */ const INVALIDATE_DOCUMENT_SCRIPT = @@ -87,14 +91,14 @@ const REFRESH_DOCUMENT_TTLS_SCRIPT = * Carry the seed's generation forward and keep every entry at or beyond the captured prefix barrier. */ const APPEND_SNAPSHOT_SCRIPT = - "local generation = redis.call('get', KEYS[2]) or ''; if generation ~= ARGV[4] or redis.call('exists', KEYS[1]) == 0 then return false end; local id = redis.call('xadd', KEYS[1], '*', ARGV[1], ARGV[2], ARGV[3], '1', ARGV[5], ARGV[4], ARGV[7], '1'); redis.call('xtrim', KEYS[1], 'MINID', ARGV[6]); return id" + "local generation = redis.call('get', KEYS[2]) or ''; if generation ~= ARGV[4] or redis.call('exists', KEYS[1]) == 0 then return false end; local id = redis.call('xadd', KEYS[1], '*', ARGV[1], ARGV[2], ARGV[3], '1', ARGV[5], ARGV[4], ARGV[7], '1', 'eu', ARGV[8], 'ec', ARGV[9], 'ei', ARGV[10]); redis.call('xtrim', KEYS[1], 'MINID', ARGV[6]); return id" /** * Atomically deduplicate and append an acknowledged client update. Socket acknowledgements can be * lost, so a retry with the same id must not inflate the stream or its compaction counters. */ const APPEND_CLIENT_UPDATE_SCRIPT = - "local generation = redis.call('get', KEYS[3]) or ''; if generation ~= ARGV[6] or redis.call('exists', KEYS[1]) == 0 then return -1 end; redis.call('expire', KEYS[4], ARGV[5]); if redis.call('zscore', KEYS[2], ARGV[1]) then redis.call('expire', KEYS[1], ARGV[5]); redis.call('expire', KEYS[3], ARGV[5]); return 0 end; local id = redis.call('xadd', KEYS[1], '*', ARGV[2], ARGV[3]); local score = string.match(id, '^(%d+)'); redis.call('zadd', KEYS[2], score, ARGV[1]); local excess = redis.call('zcard', KEYS[2]) - tonumber(ARGV[4]); if excess > 0 then redis.call('zpopmin', KEYS[2], excess) end; if redis.call('ttl', KEYS[2]) < 0 then redis.call('expire', KEYS[2], ARGV[5]) end; redis.call('expire', KEYS[1], ARGV[5]); redis.call('expire', KEYS[3], ARGV[5]); return 1" + "local generation = redis.call('get', KEYS[3]) or ''; if generation ~= ARGV[6] or redis.call('exists', KEYS[1]) == 0 then return -1 end; redis.call('expire', KEYS[4], ARGV[5]); if redis.call('zscore', KEYS[2], ARGV[1]) then redis.call('expire', KEYS[1], ARGV[5]); redis.call('expire', KEYS[3], ARGV[5]); return 0 end; local id = redis.call('xadd', KEYS[1], '*', ARGV[2], ARGV[3], 'eu', ARGV[7], 'ec', ARGV[8]); local score = string.match(id, '^(%d+)'); redis.call('zadd', KEYS[2], score, ARGV[1]); local excess = redis.call('zcard', KEYS[2]) - tonumber(ARGV[4]); if excess > 0 then redis.call('zpopmin', KEYS[2], excess) end; if redis.call('ttl', KEYS[2]) < 0 then redis.call('expire', KEYS[2], ARGV[5]) end; redis.call('expire', KEYS[1], ARGV[5]); redis.call('expire', KEYS[3], ARGV[5]); return 1" /** * Monotonic set of the synced-version token: overwrite ONLY when the new value is greater than the @@ -300,9 +304,42 @@ function isDocSeeded(doc: Y.Doc): boolean { return doc.getMap(FILE_DOC_SEED.configMap).get(FILE_DOC_SEED.flag) === true } +/** Authenticated author of a stream-accepted client edit, never a room subscriber. */ +export interface FileDocEditor { + userId: string + connectionId: string +} + +interface EditCursor { + id: string + editor: FileDocEditor | null +} + +function advanceEditor( + current: EditCursor | null, + id: string, + message: Record +): EditCursor | null { + if (!message[UPDATE_FIELD]) return current + const snapshot = message[COMPACTION_FIELD] !== undefined || message[SNAPSHOT_FIELD] !== undefined + if (!snapshot && (message[GENERATION_FIELD] || message[AGENT_FIELD])) return current + if (snapshot && !message[SNAPSHOT_FIELD]) return current + const sourceId = + snapshot && /^\d+-\d+$/.test(message.ei ?? '') && !isAfterStreamId(message.ei, id) + ? message.ei + : id + if (current && !isAfterStreamId(sourceId, current.id)) return current + const editor = + message.eu?.trim() && message.ec?.trim() + ? { userId: message.eu, connectionId: message.ec } + : null + return { id: sourceId, editor } +} + /** One locally-open room the store tracks: its doc and the last stream id applied to it. */ interface StoreRoom { doc: Y.Doc + editCursor: EditCursor | null /** The id of the last stream entry applied to `doc`; the tailer resumes strictly after it. */ lastId: string /** Local publish count, to pace compaction checks. */ @@ -338,7 +375,10 @@ export class FileDocStore { /** Dedicated connection for blocking XREAD (a blocking command monopolizes its connection). */ private read: RedisClientType | null = null private readonly rooms = new Map() - private readonly localInvalidations = new Map() + private readonly localInvalidations = new Map< + string, + { version: number; expiresAt: number; replacementDocId?: string } + >() private running = false private heartbeat: ReturnType | null = null @@ -403,6 +443,7 @@ export class FileDocStore { const room: StoreRoom = { doc, lastId: '0', + editCursor: null, publishes: 0, uncompactedDeltaBytes: 0, lastDeltaBytes: 0, @@ -564,8 +605,15 @@ export class FileDocStore { name: string, updateId: string, update: Uint8Array, - expectedGeneration = this.rooms.get(name)?.generation ?? '' + expectedGeneration = this.rooms.get(name)?.generation ?? '', + editor?: FileDocEditor ): Promise { + if ( + name.startsWith('project-file-doc:') && + (!editor?.userId.trim() || !editor.connectionId.trim()) + ) { + throw new Error('Project document updates require an authenticated editor') + } if (!this.enabled) return if (!this.write) throw new Error('FileDocStore is not initialized') assertUpdateWithinLimit(update) @@ -594,6 +642,8 @@ export class FileDocStore { String(CLIENT_UPDATE_DEDUPE_CAPACITY), String(STREAM_TTL_SEC), expectedGeneration, + editor?.userId ?? '', + editor?.connectionId ?? '', ], }) if (appended === -1) throw new FileDocInvalidatedError() @@ -632,13 +682,26 @@ export class FileDocStore { * Retries a transient Redis error like {@link appendUpdate}; throws if it ultimately fails. Disabled → * true (single-replica: seed locally, no stream). */ - async seedIfEmpty(name: string, update: Uint8Array, version = 0): Promise { + async seedIfEmpty( + name: string, + update: Uint8Array, + version = 0, + strictGeneration = false + ): Promise { assertUpdateWithinLimit(update) const generation = generationOfSeed(update) if (!this.enabled) { const invalidation = this.localInvalidations.get(name) if (invalidation && invalidation.expiresAt > Date.now() && invalidation.version > version) return false + if ( + strictGeneration && + invalidation && + invalidation.expiresAt > Date.now() && + invalidation.replacementDocId && + invalidation.replacementDocId !== generation + ) + return false const room = this.rooms.get(name) if (room) room.generationInvalidated = false return true @@ -656,6 +719,8 @@ export class FileDocStore { String(STREAM_TTL_SEC), GENERATION_FIELD, String(version), + strictGeneration ? '1' : '0', + INVALIDATED_GENERATION, ], }) await this.refreshDocumentTtls(name).catch(() => {}) @@ -721,18 +786,71 @@ export class FileDocStore { } async getDocumentGeneration(name: string): Promise { - if (!this.enabled) return '' + if (!this.enabled) { + const invalidation = this.localInvalidations.get(name) + if (invalidation && invalidation.expiresAt > Date.now()) + return invalidation.replacementDocId ?? INVALIDATED_GENERATION + const generation = this.rooms + .get(name) + ?.doc.getMap(FILE_DOC_SEED.configMap) + .get(FILE_DOC_SEED.docIdKey) + return typeof generation === 'string' ? generation : '' + } if (!this.write) throw new Error('FileDocStore is not initialized') return (await this.write.get(generationKey(name))) ?? '' } async isDocumentGenerationCurrent(name: string, generation?: string): Promise { - if (!this.enabled) return !this.rooms.get(name)?.generationInvalidated + if (!this.enabled) { + const invalidation = this.localInvalidations.get(name) + if (invalidation?.replacementDocId && invalidation.expiresAt > Date.now()) + return generation === invalidation.replacementDocId + return !this.rooms.get(name)?.generationInvalidated + } if (!this.write) throw new Error('FileDocStore is not initialized') const current = await this.write.get(generationKey(name)) return current === null ? !generation : current === generation } + /** Retire exactly one history; retries and delayed events never touch a later generation. */ + async retireDocumentGeneration( + name: string, + retiredDocId: string, + replacementDocId: string + ): Promise<{ status: 'applied'; docId: string } | { status: 'stale' }> { + if (!retiredDocId || !replacementDocId || retiredDocId === replacementDocId) + throw new Error('Document retirement requires distinct identities') + if (!this.enabled) { + if ((await this.getDocumentGeneration(name)) !== retiredDocId) return { status: 'stale' } + const previous = this.localInvalidations.get(name) + this.localInvalidations.set(name, { + version: previous?.version ?? 0, + expiresAt: Date.now() + STREAM_TTL_SEC * 1_000, + replacementDocId, + }) + const room = this.rooms.get(name) + if (room) room.generationInvalidated = true + if (!this.heartbeat) { + this.heartbeat = setInterval(() => void this.refreshTtls(), HEARTBEAT_MS) + this.heartbeat.unref() + } + return { status: 'applied', docId: retiredDocId } + } + if (!this.write) throw new Error('FileDocStore is not initialized') + const result = await this.write.eval(RETIRE_DOCUMENT_SCRIPT, { + keys: [ + streamKey(name), + generationKey(name), + `${SYNC_VERSION_PREFIX}${name}`, + `${CLIENT_UPDATE_PREFIX}${name}`, + `${AGENT_STREAM_PREFIX}${name}`, + `${INVALIDATION_VERSION_PREFIX}${name}`, + ], + arguments: [retiredDocId, replacementDocId, String(STREAM_TTL_SEC)], + }) + return typeof result === 'string' ? { status: 'applied', docId: result } : { status: 'stale' } + } + /** * Whether the file's stream already holds content — an EFFICIENCY recheck in {@link shouldSeed} that * skips the seed fetch when a prior holder already seeded (the split-brain guard itself is the atomic @@ -807,6 +925,14 @@ export class FileDocStore { * merge into and the caller should fall back to a direct file write. Disabled → always null. */ async getStreamState(name: string, expectedGeneration?: string): Promise { + return (await this.getStreamSnapshot(name, expectedGeneration))?.docState ?? null + } + + /** Reconstruct state and editor from the same stream prefix, including compacted history. */ + async getStreamSnapshot( + name: string, + expectedGeneration?: string + ): Promise<{ docState: Uint8Array; editor: FileDocEditor | null } | null> { if (!this.enabled) return null if (!this.write) throw new Error('FileDocStore is not initialized') const doc = new Y.Doc() @@ -815,18 +941,20 @@ export class FileDocStore { if (expectedGeneration !== undefined && generation !== expectedGeneration) { throw new FileDocInvalidatedError() } + const cursor: { value: EditCursor | null } = { value: null } const count = await this.replayEntries(name, '0', (entry) => { if (entry.message[GENERATION_FIELD] && entry.message[GENERATION_FIELD] !== generation) { throw new FileDocInvalidatedError() } applyEntryToDoc(doc, entry.id, entry.message) + cursor.value = advanceEditor(cursor.value, entry.id, entry.message) return true }) if ((await this.getDocumentGeneration(name)) !== generation) { throw new FileDocInvalidatedError() } if (count === 0) return null - return Y.encodeStateAsUpdate(doc) + return { docState: Y.encodeStateAsUpdate(doc), editor: cursor.value?.editor ?? null } } finally { doc.destroy() } @@ -1040,6 +1168,7 @@ export class FileDocStore { ? REDIS_AGENT_ORIGIN : REDIS_ORIGIN const seededBefore = room.seededObserved + room.editCursor = advanceEditor(room.editCursor, id, message) applyEntryToDoc(room.doc, id, message, origin) if (isDocSeeded(room.doc)) room.seededObserved = true // Track a real edit integrated from the stream so compaction knows whether its snapshot represents @@ -1176,6 +1305,7 @@ export class FileDocStore { // them — only entries the snapshot provably subsumes (id <= lastId). Trimming to the freshly // appended snapshot id instead would silently drop those un-integrated peer entries. const upTo = room.lastId + const editCursor = room.editCursor /** Ordered, deduplicated replay lets two counters represent the prefix without a per-entry map. */ const deltaBytesAtBarrier = room.uncompactedDeltaBytes - room.lastDeltaBytes const snapshot = Buffer.from(Y.encodeStateAsUpdate(room.doc)).toString('base64') @@ -1194,6 +1324,9 @@ export class FileDocStore { GENERATION_FIELD, upTo, COMPACTION_FIELD, + editCursor?.editor?.userId ?? '', + editCursor?.editor?.connectionId ?? '', + editCursor?.id ?? '', ], }) if (typeof snapshotId !== 'string') return diff --git a/apps/realtime/src/handlers/file-doc.ts b/apps/realtime/src/handlers/file-doc.ts index 994a19ce014..c8aefc15932 100644 --- a/apps/realtime/src/handlers/file-doc.ts +++ b/apps/realtime/src/handlers/file-doc.ts @@ -33,6 +33,7 @@ import { FILE_DOC_SCHEMA_VERSION, FILE_DOC_SEED, FILE_DOC_TIMEOUTS, + type FileDocPermission, type FileDocPresenceUser, type FileDocUpdateAck, type FileDocUpdatePayload, @@ -40,7 +41,13 @@ import { type LeaveFileDocPayload, toFileDocBytes, } from '@sim/realtime-protocol/file-doc' -import { ROOM_TYPES, type RoomRef, roomName } from '@sim/realtime-protocol/rooms' +import { + projectFileDocRoom, + projectFileDocTarget, + ROOM_TYPES, + type RoomRef, + roomName, +} from '@sim/realtime-protocol/rooms' import { getErrorMessage } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import * as decoding from 'lib0/decoding' @@ -50,8 +57,15 @@ import * as awarenessProtocol from 'y-protocols/awareness' import * as syncProtocol from 'y-protocols/sync' import * as Y from 'yjs' import { resolveAvatarUrl } from '@/handlers/avatar' -import { fetchFileDocMerge, fetchFileDocPersist, fetchFileDocSeed } from '@/handlers/file-doc-app' import { + fetchFileDocMerge, + fetchFileDocPersist, + fetchFileDocSeed, + fetchProjectFileDocPersist, + fetchProjectFileDocSeed, +} from '@/handlers/file-doc-app' +import { + type FileDocEditor, FileDocInvalidatedError, getFileDocStore, REDIS_AGENT_ORIGIN, @@ -132,6 +146,8 @@ interface FileDocOwner { interface FileDocRoom { /** The `workspace_files.id` this room edits. */ fileId: string + projectId?: string + lastEditorConnectionId: string | null doc: Y.Doc awareness: awarenessProtocol.Awareness /** socketId → (clientId → its presence ownership). A socket owns one entry per collaborative provider @@ -222,14 +238,17 @@ interface AwarenessChange { removed: number[] } -const fileDocRoom = (fileId: string): RoomRef => ({ - type: ROOM_TYPES.WORKSPACE_FILE_DOC, - id: fileId, -}) +const fileDocRoom = (fileId: string, projectId?: string): RoomRef => + projectId + ? projectFileDocRoom(projectId, fileId) + : { + type: ROOM_TYPES.WORKSPACE_FILE_DOC, + id: fileId, + } /** Pending admissions receive invalidations here, never document or presence frames. */ -export function fileDocAdmissionRoom(fileId: string): string { - return `file-doc-admission:${fileId}` +export function fileDocAdmissionRoom(fileId: string, projectId?: string): string { + return `file-doc-admission:${projectId ? `${projectId}/` : ''}${fileId}` } /** @@ -327,7 +346,12 @@ function broadcastLocal( * last-disconnect is separate ({@link flushPersist} with `final`). */ function schedulePersist(name: string, room: FileDocRoom): void { - if (!room.workspaceId || !room.lastEditorUserId) return + if ( + room.projectId + ? !getFileDocStore().enabled && !room.lastEditorConnectionId + : !room.workspaceId || !room.lastEditorUserId + ) + return const now = Date.now() if (room.persistDeadline === null) room.persistDeadline = now + PERSIST_MAX_WAIT_MS if (room.persistTimer) clearTimeout(room.persistTimer) @@ -363,11 +387,22 @@ function flushPersist(name: string, room: FileDocRoom, final: boolean): Promise< async function persistRoom(name: string, room: FileDocRoom, final: boolean): Promise { // Never project a doc no user actually edited back over the file (see {@link FileDocRoom.edited}). - if (!room.edited || !room.workspaceId || !room.lastEditorUserId) return + if ( + !room.edited || + (room.projectId + ? !getFileDocStore().enabled && !room.lastEditorConnectionId + : !room.workspaceId || !room.lastEditorUserId) + ) + return const store = getFileDocStore() const generation = docIdOf(room.doc) const workspaceId = room.workspaceId const userId = room.lastEditorUserId + const localEditor: FileDocEditor | null = + userId && room.lastEditorConnectionId + ? { userId, connectionId: room.lastEditorConnectionId } + : null + let projectEditor = localEditor // Synchronous fallback capture — before any await, since the caller may destroy `room.doc` the moment // this yields. Only meaningful once seeded; used only when the authoritative stream state is absent. const localState = isDocSeeded(room.doc) ? Y.encodeStateAsUpdate(room.doc) : null @@ -381,14 +416,27 @@ async function persistRoom(name: string, room: FileDocRoom, final: boolean): Pro // snapshot captured before the reconcile mutated it (which — with the version already advanced — // would let the If-Match pass and clobber the reconciled edit). Fall back to that snapshot once the // room is torn down (last-leave), where the doc is gone and there is nothing left to reconcile. - return fileDocRooms.get(name) === room && isDocSeeded(room.doc) - ? Y.encodeStateAsUpdate(room.doc) - : localState + if (fileDocRooms.get(name) === room && isDocSeeded(room.doc)) { + if (room.projectId) { + projectEditor = + room.lastEditorUserId && room.lastEditorConnectionId + ? { userId: room.lastEditorUserId, connectionId: room.lastEditorConnectionId } + : null + } + return Y.encodeStateAsUpdate(room.doc) + } + if (room.projectId) projectEditor = localEditor + return localState } try { + if (room.projectId) { + const snapshot = await store.getStreamSnapshot(name, generation) + projectEditor = snapshot?.editor ?? null + return snapshot?.docState ?? null + } return (await store.getStreamState(name)) ?? localState } catch (streamError) { - if (streamError instanceof FileDocInvalidatedError) throw streamError + if (room.projectId || streamError instanceof FileDocInvalidatedError) throw streamError // A transient Redis read must NOT drop the write when we already hold a valid local snapshot — // else the last-disconnect flush loses the session's edits as the room is torn down. But once a // reconcile has run, `localState` is NULLED (it predates the merged-in out-of-band edit), so a @@ -439,7 +487,18 @@ async function persistRoom(name: string, room: FileDocRoom, final: boolean): Pro const docState = await captureState() if (!docState) return // nothing seeded/authoritative to persist yet if (!(await store.isDocumentGenerationCurrent(name, generation))) return - const result = await fetchFileDocPersist(workspaceId, room.fileId, userId, docState, ifMatch) + if (room.projectId && !projectEditor) return + const result = + room.projectId && projectEditor + ? await fetchProjectFileDocPersist( + { projectId: room.projectId, fileId: room.fileId, ...projectEditor }, + docState, + ifMatch + ) + : workspaceId && userId + ? await fetchFileDocPersist(workspaceId, room.fileId, userId, docState, ifMatch) + : null + if (!result) return if (result.status === 'missing') return // the file was deleted; nothing to write if (result.status === 'deferred') { // No version token available (momentarily — a Redis blip on a peer-seeded task). Leave the edits in @@ -598,14 +657,16 @@ export async function flushAllFileDocRooms(): Promise { async function ensureRoomReady( name: string, room: FileDocRoom, - workspaceId: string | null + workspaceId: string | null, + actor?: FileDocEditor ): Promise { await room.hydrated // The room can be dropped and re-created while the catch-up is in flight (a fast open→close); the // join re-checks identity after this and abandons a stale room rather than serving from it. if (fileDocRooms.get(name) !== room) return - if (!workspaceId) throw new Error(`File document ${room.fileId} has no workspace context`) - await ensureServerSeed(name, room, workspaceId) + if (!room.projectId && !workspaceId) + throw new Error(`File document ${room.fileId} has no owner context`) + await ensureServerSeed(name, room, workspaceId, actor) if (fileDocRooms.get(name) === room && !isDocSeeded(room.doc)) { throw new Error(`File document ${room.fileId} could not be seeded`) } @@ -616,9 +677,14 @@ async function ensureRoomReady( * Clear settled attempts to allow retry after transient failures. Existing empty files have a named * seed; a missing file must fail admission rather than create an editable blank room. */ -function ensureServerSeed(name: string, room: FileDocRoom, workspaceId: string): Promise { +function ensureServerSeed( + name: string, + room: FileDocRoom, + workspaceId: string | null, + actor?: FileDocEditor +): Promise { if (isDocSeeded(room.doc)) return Promise.resolve() - room.seeding ??= runServerSeed(name, room, workspaceId).finally(() => { + room.seeding ??= runServerSeed(name, room, workspaceId, actor).finally(() => { room.seeding = null }) return room.seeding @@ -636,7 +702,12 @@ const SEED_WAIT_RETRY_MS = 150 class FileDocNotFoundError extends Error {} -async function runServerSeed(name: string, room: FileDocRoom, workspaceId: string): Promise { +async function runServerSeed( + name: string, + room: FileDocRoom, + workspaceId: string | null, + actor?: FileDocEditor +): Promise { const store = getFileDocStore() const deadline = Date.now() + FILE_DOC_TIMEOUTS.seedRequestMs while (fileDocRooms.get(name) === room && !isDocSeeded(room.doc)) { @@ -644,7 +715,7 @@ async function runServerSeed(name: string, room: FileDocRoom, workspaceId: strin // fix for split-brain seeding). Returns a lock token here (single-pod: a sentinel token). const token = await store.shouldSeed(name) if (token) { - await seedUnderLock(name, room, workspaceId, token) + await seedUnderLock(name, room, workspaceId, token, actor) return } // No token: a peer holds the lock with its fetch in flight, or the stream is already seeded (which @@ -660,20 +731,32 @@ async function runServerSeed(name: string, room: FileDocRoom, workspaceId: strin async function seedUnderLock( name: string, room: FileDocRoom, - workspaceId: string, - token: string + workspaceId: string | null, + token: string, + actor?: FileDocEditor ): Promise { const store = getFileDocStore() // Release the lock on EVERY exit from here (one `finally`, impossible to leak). try { - const seed = await fetchFileDocSeed(workspaceId, room.fileId) + const seed = + room.projectId && actor + ? await fetchProjectFileDocSeed({ + projectId: room.projectId, + fileId: room.fileId, + ...actor, + }) + : workspaceId + ? await fetchFileDocSeed(workspaceId, room.fileId) + : null if (fileDocRooms.get(name) !== room || isDocSeeded(room.doc)) return if (!seed) throw new FileDocNotFoundError('File not found') /** * Publish before local apply: the atomic empty-stream append, not the expiring lock, prevents * independent Yjs histories from entering the same room. */ - const didSeed = await store.seedIfEmpty(name, seed.update, seed.version) + const didSeed = room.projectId + ? await store.seedIfEmpty(name, seed.update, seed.version, true) + : await store.seedIfEmpty(name, seed.update, seed.version) /** * Record only our winning seed's version before the readiness guard: the tailer may already have * applied it during the append, but persistence still needs the matching local version. @@ -736,9 +819,10 @@ interface MergeOrder { export function applyMarkdownToLiveFileDoc( fileId: string, markdown: string, - order: MergeOrder = {} + order: MergeOrder = {}, + projectId?: string ): Promise<'applied' | 'no-live-room' | 'merge-unavailable' | 'stale'> { - const name = roomName(fileDocRoom(fileId)) + const name = roomName(fileDocRoom(fileId, projectId)) return serializeFileDocMutation(name, () => mergeMarkdownIntoRoom(name, fileId, markdown, order)) } @@ -767,9 +851,10 @@ async function acquireFileDocMergeSlot(name: string): Promise { /** Serializes and version-orders an unsupported durable replacement with live Markdown merges. */ export function invalidateLiveFileDocument( fileId: string, - version: number + version: number, + projectId?: string ): Promise<{ status: 'applied'; docId?: string } | { status: 'stale' }> { - const name = roomName(fileDocRoom(fileId)) + const name = roomName(fileDocRoom(fileId, projectId)) return serializeFileDocMutation(name, async () => { const store = getFileDocStore() const token = await acquireFileDocMergeSlot(name) @@ -783,6 +868,42 @@ export function invalidateLiveFileDocument( }) } +/** Lifecycle retirement compares identities, independently of the unchanged Markdown timestamp. */ +export function retireLiveProjectFileDocument( + target: { projectId: string; fileId: string; retiredDocId: string; replacementDocId: string }, + io: Server +): Promise<{ status: 'applied'; docId: string } | { status: 'stale' }> { + const name = roomName(projectFileDocRoom(target.projectId, target.fileId)) + return serializeFileDocMutation(name, async () => { + const store = getFileDocStore() + const token = await acquireFileDocMergeSlot(name) + if (!token) throw new Error('Project document retirement slot is temporarily unavailable') + try { + const result = await store.retireDocumentGeneration( + name, + target.retiredDocId, + target.replacementDocId + ) + if (result.status === 'applied') { + io.to([name, fileDocAdmissionRoom(target.fileId, target.projectId)]).emit( + FILE_DOC_EVENTS.INVALIDATED, + { + fileId: target.fileId, + docId: target.retiredDocId, + message: 'This document was archived or restored. Reload to continue.', + } + ) + } + const existing = fileDocRooms.get(name) + if (existing && docIdOf(existing.doc) === target.retiredDocId) + discardInvalidatedRoom(name, io) + return result + } finally { + await store.releaseMergeSlot(name, token) + } + }) +} + async function mergeMarkdownIntoRoom( name: string, fileId: string, @@ -899,7 +1020,9 @@ function getOrCreateRoom(io: Server, ref: RoomRef): FileDocRoom { // Started BEFORE the room is registered so no join can observe a room without its hydration handle. const hydrated = getFileDocStore().attachRoom(name, doc) const room: FileDocRoom = { - fileId: ref.id, + fileId: projectFileDocTarget(ref)?.fileId ?? ref.id, + projectId: projectFileDocTarget(ref)?.projectId, + lastEditorConnectionId: null, doc, awareness, owners: new Map(), @@ -1010,13 +1133,6 @@ function emitJoinError( }) } -/** - * The permission occupying a file-doc room requires — `write`, since the room IS - * the collaborative editor. Sourced from the shared map so the join check, the - * per-frame gate below, and the re-validation sweep can never drift apart. - */ -const FILE_DOC_ACTION = ROOM_MEMBERSHIP_ACTIONS[ROOM_TYPES.WORKSPACE_FILE_DOC] - /** * Per-frame authorization for a socket's inbound document/awareness frames. * @@ -1035,16 +1151,40 @@ const FILE_DOC_ACTION = ROOM_MEMBERSHIP_ACTIONS[ROOM_TYPES.WORKSPACE_FILE_DOC] * join records one, so the exposure is bounded by the same TTL the workflow * write-path has always had, and the sweep evicts independently. */ -function isFileDocWriteAllowed(socket: AuthenticatedSocket, io: Server, name: string): boolean { +function isFileDocWriteAllowed( + socket: AuthenticatedSocket, + io: Server, + name: string +): boolean | Promise { const userId = socket.userId const fileId = fileDocRooms.get(name)?.fileId if (!userId || !fileId) return false - const room = fileDocRoom(fileId) + const projectId = fileDocRooms.get(name)?.projectId + const room = fileDocRoom(fileId, projectId) + if (projectId) + return (async () => { + const permission = await resolveCurrentRoomPermission(userId, room, 'read', socket.id) + if (socketToRoomName.get(socket.id) !== name) return false + const canWrite = permission === 'write' || permission === 'admin' + socket.emit(FILE_DOC_EVENTS.PERMISSION, { + fileId, + projectId, + canWrite, + } satisfies FileDocPermission) + if (permission === null) + evictSocketFromRoom(socket, room, 'Your access to this document has been revoked', io) + return canWrite + })() const cached = peekRoomPermission(userId, room) if (cached === undefined) { // Single-flighted, so a burst of frames triggers at most one query. - void resolveCurrentRoomPermission(userId, room, FILE_DOC_ACTION).catch(() => {}) + void resolveCurrentRoomPermission( + userId, + room, + ROOM_MEMBERSHIP_ACTIONS[room.type], + socket.id + ).catch(() => {}) return true } if (satisfiesRoomMembership(cached, ROOM_TYPES.WORKSPACE_FILE_DOC)) return true @@ -1066,17 +1206,18 @@ function isFileDocWriteAllowed(socket: AuthenticatedSocket, io: Server, name: st * revocation and left the Socket.IO room). Scoped to the evicted room so a socket * that has since switched documents keeps the one it legitimately holds. */ -registerRoomEvictionHandler(ROOM_TYPES.WORKSPACE_FILE_DOC, (socketId, room, io) => { - if (socketToRoomName.get(socketId) !== roomName(room)) return - cleanupFileDocForSocket(socketId, io) -}) +for (const type of [ROOM_TYPES.WORKSPACE_FILE_DOC, ROOM_TYPES.PROJECT_FILE_DOC]) + registerRoomEvictionHandler(type, (socketId, room, io) => { + if (socketToRoomName.get(socketId) !== roomName(room)) return + cleanupFileDocForSocket(socketId, io) + }) -function handleMessage(socket: AuthenticatedSocket, io: Server, data: unknown) { +async function handleMessage(socket: AuthenticatedSocket, io: Server, data: unknown) { const name = socketToRoomName.get(socket.id) if (!name) return const room = fileDocRooms.get(name) if (!room) return - if (!isFileDocWriteAllowed(socket, io, name)) return + if (!room.projectId && !isFileDocWriteAllowed(socket, io, name)) return const bytes = toFileDocBytes(data) if (!bytes) return @@ -1100,13 +1241,33 @@ function handleMessage(socket: AuthenticatedSocket, io: Server, data: unknown) { } const decoder = decoding.createDecoder(bytes) const messageType = decoding.readVarUint(decoder) + if (room.projectId) { + const userId = socket.userId + if (!userId) return + const permission = await resolveCurrentRoomPermission( + userId, + fileDocRoom(room.fileId, room.projectId), + 'read', + socket.id + ) + if (!permission || socketToRoomName.get(socket.id) !== name) return + if (messageType === FILE_DOC_MESSAGE_TYPE.SYNC) { + const peek = decoding.createDecoder(bytes) + decoding.readVarUint(peek) + if (decoding.readVarUint(peek) !== syncProtocol.messageYjsSyncStep1) return + } else if ( + messageType !== FILE_DOC_MESSAGE_TYPE.AWARENESS && + !(await isFileDocWriteAllowed(socket, io, name)) + ) + return + } switch (messageType) { case FILE_DOC_MESSAGE_TYPE.SYNC: { // Attribute a server-side persist of the resulting edit to the actual editor (blob metadata). A // socket's providers are all the same user, so any owner's userId identifies the editor. const editor = room.owners.get(socket.id)?.values().next().value?.userId - if (editor) room.lastEditorUserId = editor + if (editor && !room.projectId) room.lastEditorUserId = editor const encoder = encoding.createEncoder() encoding.writeVarUint(encoder, FILE_DOC_MESSAGE_TYPE.SYNC) // `socket.id` is the transaction origin, so the doc's `update` handler @@ -1183,6 +1344,11 @@ async function handleClientUpdate( if ( typeof candidate.fileId !== 'string' || candidate.fileId.length === 0 || + (candidate.projectId !== undefined && + (typeof candidate.projectId !== 'string' || + !candidate.projectId || + /[/:]/.test(candidate.projectId) || + /[/:]/.test(candidate.fileId))) || typeof candidate.docId !== 'string' || candidate.docId.length === 0 || typeof candidate.updateId !== 'string' || @@ -1197,7 +1363,7 @@ async function handleClientUpdate( } const name = socketToRoomName.get(socket.id) - if (!name || name !== roomName(fileDocRoom(candidate.fileId))) { + if (!name || name !== roomName(fileDocRoom(candidate.fileId, candidate.projectId))) { reject('NOT_JOINED', true, candidate.updateId) return } @@ -1206,7 +1372,9 @@ async function handleClientUpdate( reject('NOT_JOINED', true, candidate.updateId) return } - if (!isFileDocWriteAllowed(socket, io, name)) { + const admission = isFileDocWriteAllowed(socket, io, name) + const allowed = typeof admission === 'boolean' ? admission : await admission + if (!allowed) { reject('ACCESS_REVOKED', false, candidate.updateId) return } @@ -1232,17 +1400,25 @@ async function handleClientUpdate( } const editor = room.owners.get(socket.id)?.values().next().value?.userId - if (editor) room.lastEditorUserId = editor + if (!editor) { + reject('ACCESS_REVOKED', false, candidate.updateId) + return + } room.pendingUpdates += 1 try { const store = getFileDocStore() - await store.publishClientUpdateAndWait(name, candidate.updateId, update, candidate.docId) + await store.publishClientUpdateAndWait(name, candidate.updateId, update, candidate.docId, { + userId: editor, + connectionId: socket.id, + }) if ( !(await store.isDocumentGenerationCurrent(name, candidate.docId)) || fileDocRooms.get(name) !== room ) { throw new FileDocInvalidatedError() } + room.lastEditorUserId = editor + room.lastEditorConnectionId = socket.id Y.applyUpdate(room.doc, update, clientUpdateOrigin(socket.id)) room.edited = true schedulePersist(name, room) @@ -1330,7 +1506,7 @@ export function setupWorkspaceFileDocHandlers( const pendingMemberships = new Map() socket.on(FILE_DOC_EVENTS.JOIN, async (payload: JoinFileDocPayload) => { - const { fileId, clientId } = payload + const { fileId, clientId, projectId } = payload // Hoisted so the catch can tell whether this join was superseded (a switch to another file) // before surfacing a retryable error for the abandoned one. let generation: number | undefined @@ -1364,6 +1540,11 @@ export function setupWorkspaceFileDocHandlers( if ( typeof fileId !== 'string' || fileId.length === 0 || + (projectId !== undefined && + (typeof projectId !== 'string' || + !projectId || + /[/:]/.test(projectId) || + /[/:]/.test(fileId))) || // A Yjs clientID is a uint32; reject malformed values before they can become ownership keys. !isYjsClientId(clientId) ) { @@ -1393,24 +1574,27 @@ export function setupWorkspaceFileDocHandlers( generation = joinGeneration.get(socket.id) ?? 0 } - const room = fileDocRoom(fileId) + const room = fileDocRoom(fileId, projectId) const name = roomName(room) - const admissionName = fileDocAdmissionRoom(fileId) - - const authorized = await resolveRoomJoinAuth({ - userId, - room, - action: FILE_DOC_ACTION, - logger, - logLabel: `file-doc room for ${userId}`, - messages: { - verifyFailed: 'Failed to verify workspace access', - notFound: 'File not found', - accessDenied: 'Access denied to file', - }, - emitError: ({ error, code, retryable }) => - emitJoinError(socket, fileId, clientId, error, code, retryable), - }) + const admissionName = fileDocAdmissionRoom(fileId, projectId) + + const authorizeJoin = () => + resolveRoomJoinAuth({ + userId, + connectionId: socket.id, + room, + action: ROOM_MEMBERSHIP_ACTIONS[room.type], + logger, + logLabel: `file-doc room for ${userId}`, + messages: { + verifyFailed: 'Failed to verify workspace access', + notFound: 'File not found', + accessDenied: 'Access denied to file', + }, + emitError: ({ error, code, retryable }) => + emitJoinError(socket, fileId, clientId, error, code, retryable), + }) + const authorized = await authorizeJoin() if (!authorized) return // Server-authenticated identity for the presence roster (never trusts the client-set @@ -1418,6 +1602,23 @@ export function setupWorkspaceFileDocHandlers( const avatarUrl = await resolveAvatarUrl(socket, userId) const store = getFileDocStore() + if (projectId && authorized.docId) { + const shared = await store.getDocumentGeneration(name) + if (shared && shared !== authorized.docId) { + await retireLiveProjectFileDocument( + { + projectId, + fileId, + retiredDocId: shared, + replacementDocId: authorized.docId, + }, + io + ) + } + const local = fileDocRooms.get(name) + if (local && docIdOf(local.doc) && docIdOf(local.doc) !== authorized.docId) + discardInvalidatedRoom(name, io) + } const existing = fileDocRooms.get(name) if ( existing && @@ -1444,7 +1645,7 @@ export function setupWorkspaceFileDocHandlers( const canRegisterJoin = () => { if (!isCurrentJoin()) return false const permission = peekRoomPermission(userId, room) - if (satisfiesRoomMembership(permission ?? null, ROOM_TYPES.WORKSPACE_FILE_DOC)) return true + if (satisfiesRoomMembership(permission ?? null, room.type)) return true emitJoinError( socket, fileId, @@ -1462,7 +1663,7 @@ export function setupWorkspaceFileDocHandlers( // document's history, and it watches that replay on screen (reload right after moving a block // and the block moves again in front of you). Waiting here is what makes the handshake below // authoritative: the client's first sync IS the finished document, in one message. - await ensureRoomReady(name, entry, entry.workspaceId) + await ensureRoomReady(name, entry, entry.workspaceId, { userId, connectionId: socket.id }) // Re-check access immediately before registering, mirroring the workflow join: the // access re-validation sweep records a revocation BEFORE it evicts, so a join that @@ -1471,8 +1672,13 @@ export function setupWorkspaceFileDocHandlers( // peek treats an expired entry as unknown and fails open, which a join stalled // longer than the cache TTL would slip straight through. Normally a cache hit (this // join's own authorize just warmed it), so it costs no extra query. - const currentPermission = await resolveCurrentRoomPermission(userId, room, FILE_DOC_ACTION) - if (!satisfiesRoomMembership(currentPermission, ROOM_TYPES.WORKSPACE_FILE_DOC)) { + const currentPermission = await resolveCurrentRoomPermission( + userId, + room, + ROOM_MEMBERSHIP_ACTIONS[room.type], + socket.id + ) + if (!satisfiesRoomMembership(currentPermission, room.type)) { logger.warn( `User ${userId} lost write access to file ${fileId} before the join completed` ) @@ -1491,8 +1697,13 @@ export function setupWorkspaceFileDocHandlers( const joinedVersion = Math.max(entry.syncedVersion ?? 0, (await store.getSyncedVersion(name)) ?? 0) || undefined /** Adapter membership can wait; resolve access again before checking the final generation. */ - const finalPermission = await resolveCurrentRoomPermission(userId, room, FILE_DOC_ACTION) - if (!satisfiesRoomMembership(finalPermission, ROOM_TYPES.WORKSPACE_FILE_DOC)) { + let finalPermission = await resolveCurrentRoomPermission( + userId, + room, + ROOM_MEMBERSHIP_ACTIONS[room.type], + socket.id + ) + if (!satisfiesRoomMembership(finalPermission, room.type)) { emitJoinError(socket, fileId, clientId, 'Access denied to file', 'ACCESS_DENIED', false) return } @@ -1511,6 +1722,23 @@ export function setupWorkspaceFileDocHandlers( } if (!canRegisterJoin()) return await socket.join(name) + if (projectId) { + const currentAccess = await authorizeJoin() + if (!currentAccess) return + if (!isCurrentJoin()) return + if (!currentAccess.docId || currentAccess.docId !== docIdOf(entry.doc)) { + emitJoinError( + socket, + fileId, + clientId, + 'Document changed while joining', + 'JOIN_FAILED', + true + ) + return + } + finalPermission = currentAccess.workspacePermission + } /** Adapter joins can wait; recheck access and liveness before ownership or synchronization. */ if (!canRegisterJoin()) return @@ -1578,7 +1806,7 @@ export function setupWorkspaceFileDocHandlers( // Attribution for the server-side persist, refreshed to the actual editor on each edit in // `handleMessage`. - entry.lastEditorUserId = userId + if (!projectId) entry.lastEditorUserId = userId // Name the document this room holds, so a client that still carries a DIFFERENT one (its room // outlived by a document rebuilt in its place) can refuse to merge instead of unioning two @@ -1589,7 +1817,10 @@ export function setupWorkspaceFileDocHandlers( docId: docIdOf(entry.doc), version: joinedVersion, schemaVersion: FILE_DOC_SCHEMA_VERSION, - ...(store.enabled ? { acknowledgedUpdates: true as const } : {}), + ...(store.enabled || projectId ? { acknowledgedUpdates: true as const } : {}), + ...(projectId + ? { projectId, canWrite: finalPermission === 'write' || finalPermission === 'admin' } + : {}), }) // Server-authenticated roster → everyone in the room, including this joiner. broadcastFileDocPresence(io, name, entry) @@ -1632,7 +1863,7 @@ export function setupWorkspaceFileDocHandlers( } catch (error) { logger.error('Error joining file-doc room:', error) try { - const name = roomName(fileDocRoom(fileId)) + const name = roomName(fileDocRoom(fileId, projectId)) /** * Roll back ownership only if this attempt committed it. A failed provisional admission must * preserve a previous file's binding and any co-mounted provider already in the target room. @@ -1690,7 +1921,8 @@ export function setupWorkspaceFileDocHandlers( if (!name) return // Scope the leave to the named file when provided: a deferred leave from a // prior document must not evict the socket from one it has since opened. - if (payload?.fileId && roomName(fileDocRoom(payload.fileId)) !== name) return + if (payload?.fileId && roomName(fileDocRoom(payload.fileId, payload.projectId)) !== name) + return socket.leave(name) cleanupFileDocForSocket(socket.id, io) } catch (error) { diff --git a/apps/realtime/src/handlers/room-join-auth.ts b/apps/realtime/src/handlers/room-join-auth.ts index f54d6a81782..593c3b069b2 100644 --- a/apps/realtime/src/handlers/room-join-auth.ts +++ b/apps/realtime/src/handlers/room-join-auth.ts @@ -1,12 +1,14 @@ import type { createLogger } from '@sim/logger' import { authorizeRoom } from '@sim/platform-authz/rooms' -import type { RoomRef } from '@sim/realtime-protocol/rooms' +import { projectFileDocTarget, ROOM_TYPES, type RoomRef } from '@sim/realtime-protocol/rooms' +import { fetchProjectFileDocAccess } from '@/handlers/file-doc-app' import { beginRoomPermissionRead, commitRoomPermission } from '@/middleware/permissions' -type Authorized = Awaited> +type Authorized = Awaited> & { docId?: string | null } interface ResolveRoomJoinAuthParams { userId: string + connectionId?: string room: RoomRef action: 'read' | 'write' logger: ReturnType @@ -40,7 +42,18 @@ export async function resolveRoomJoinAuth( // is never overwritten by this older result — see {@link commitRoomPermission}. const readSeq = beginRoomPermissionRead() try { - authorized = await authorizeRoom({ userId, room, action }) + if (room.type === ROOM_TYPES.PROJECT_FILE_DOC) { + const target = projectFileDocTarget(room) + if (!target || !params.connectionId) + throw new Error('Project document requires a socket identity') + authorized = await fetchProjectFileDocAccess({ + ...target, + userId, + connectionId: params.connectionId, + }) + } else { + authorized = await authorizeRoom({ userId, room, action }) + } } catch (error) { logger.warn(`Error authorizing ${logLabel}:`, error) emitError({ error: messages.verifyFailed, code: 'VERIFY_ACCESS_FAILED', retryable: true }) diff --git a/apps/realtime/src/middleware/permissions.ts b/apps/realtime/src/middleware/permissions.ts index a007de6c8b2..8581a99d3db 100644 --- a/apps/realtime/src/middleware/permissions.ts +++ b/apps/realtime/src/middleware/permissions.ts @@ -13,8 +13,14 @@ import { VARIABLE_OPERATIONS, WORKFLOW_OPERATIONS, } from '@sim/realtime-protocol/constants' -import { ROOM_TYPES, type RoomRef, roomName } from '@sim/realtime-protocol/rooms' +import { + projectFileDocTarget, + ROOM_TYPES, + type RoomRef, + roomName, +} from '@sim/realtime-protocol/rooms' import { and, eq, isNull } from 'drizzle-orm' +import { fetchProjectFileDocAccess } from '@/handlers/file-doc-app' const logger = createLogger('SocketPermissions') @@ -219,8 +225,15 @@ function commitRoleDecision(key: string, role: string | null, readSeq: number): */ async function readAuthoritativeRoomPermission( userId: string, - room: RoomRef + room: RoomRef, + connectionId?: string ): Promise { + if (room.type === ROOM_TYPES.PROJECT_FILE_DOC) { + const target = projectFileDocTarget(room) + if (!target || !connectionId) throw new Error('Project document requires a socket identity') + const authorization = await fetchProjectFileDocAccess({ ...target, userId, connectionId }) + return authorization.allowed ? authorization.workspacePermission : null + } if (room.type === ROOM_TYPES.WORKFLOW) { const authorization = await authorizeWorkflowByWorkspacePermission({ workflowId: room.id, @@ -241,11 +254,12 @@ async function resolveRoleUncached( key: string, userId: string, room: RoomRef, - fallbackRole: string + fallbackRole: string, + connectionId?: string ): Promise { const readSeq = beginRoomPermissionRead() try { - const role = await readAuthoritativeRoomPermission(userId, room) + const role = await readAuthoritativeRoomPermission(userId, room, connectionId) // Yields only to a decision from a later-STARTED read (see commitRoleDecision). // Comparing write order instead would let a join whose authorize began before // this one — but returned after it — bury this result. @@ -259,6 +273,7 @@ async function resolveRoleUncached( // already-revoked user — so a recorded revocation survives a transient DB failure // instead of reverting to the stale join-time role. Only trust `fallbackRole` when // nothing has been recorded for this (user, workflow) yet. + if (room.type === ROOM_TYPES.PROJECT_FILE_DOC) return null const lastKnown = roleCache.get(key) return lastKnown !== undefined ? lastKnown.role : fallbackRole } @@ -279,7 +294,8 @@ async function resolveRoleUncached( export async function resolveCurrentRoomPermission( userId: string, room: RoomRef, - fallbackRole: string + fallbackRole: string, + connectionId?: string ): Promise { const key = roleCacheKey(userId, room) const cached = roleCache.get(key) @@ -292,9 +308,11 @@ export async function resolveCurrentRoomPermission( return inFlight } - const resolution = resolveRoleUncached(key, userId, room, fallbackRole).finally(() => { - inFlightRoleResolutions.delete(key) - }) + const resolution = resolveRoleUncached(key, userId, room, fallbackRole, connectionId).finally( + () => { + inFlightRoleResolutions.delete(key) + } + ) inFlightRoleResolutions.set(key, resolution) return resolution } diff --git a/apps/realtime/src/routes/http.ts b/apps/realtime/src/routes/http.ts index da26b68aa3b..c7ed19e10e8 100644 --- a/apps/realtime/src/routes/http.ts +++ b/apps/realtime/src/routes/http.ts @@ -1,12 +1,18 @@ import type { IncomingMessage, ServerResponse } from 'http' import { FILE_DOC_EVENTS, type FileDocInvalidated } from '@sim/realtime-protocol/file-doc' -import { ROOM_TYPES, roomName, WORKSPACE_LIST_ROOM_TYPES } from '@sim/realtime-protocol/rooms' +import { + projectFileDocRoom, + ROOM_TYPES, + roomName, + WORKSPACE_LIST_ROOM_TYPES, +} from '@sim/realtime-protocol/rooms' import { safeCompare } from '@sim/security/compare' import { env } from '@/env' import { applyMarkdownToLiveFileDoc, fileDocAdmissionRoom, invalidateLiveFileDocument, + retireLiveProjectFileDocument, } from '@/handlers/file-doc' import { type IRoomManager, WorkflowRoomService } from '@/rooms' @@ -202,15 +208,29 @@ export function createHttpHandler(roomManager: IRoomManager, logger: Logger) { if (req.method === 'POST' && req.url === '/api/file-doc/apply-edit') { try { const body = await readRequestBody(req) - const { fileId, markdown, version } = JSON.parse(body) + const { fileId, markdown, version, owner } = JSON.parse(body) if (!isNonEmptyString(fileId) || typeof markdown !== 'string') { return sendError(res, 'Invalid fileId or markdown', 400) } + if ( + owner !== undefined && + (!owner || + owner.entityType !== 'project' || + !isNonEmptyString(owner.entityId) || + /[/:]/.test(owner.entityId) || + /[/:]/.test(fileId)) + ) + return sendError(res, 'Invalid file owner', 400) // `version` (the durable updatedAt this markdown was written with) records that the live doc now // incorporates that durable version, so the persist If-Match guard won't flag it as a conflict. - const result = await applyMarkdownToLiveFileDoc(fileId, markdown, { - version: typeof version === 'number' ? version : undefined, - }) + const result = await applyMarkdownToLiveFileDoc( + fileId, + markdown, + { + version: typeof version === 'number' ? version : undefined, + }, + owner?.entityId + ) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ applied: result === 'applied', status: result })) } catch (error) { @@ -220,16 +240,57 @@ export function createHttpHandler(roomManager: IRoomManager, logger: Logger) { return } + if (req.method === 'POST' && req.url === '/api/file-doc/retire') { + try { + const { projectId, fileId, retiredDocId, replacementDocId } = JSON.parse( + await readRequestBody(req) + ) + if ( + !isNonEmptyString(projectId) || + /[/:]/.test(projectId) || + !isNonEmptyString(fileId) || + /[/:]/.test(fileId) || + !isNonEmptyString(retiredDocId) || + retiredDocId.length > 128 || + !isNonEmptyString(replacementDocId) || + replacementDocId.length > 128 || + retiredDocId === replacementDocId + ) + return sendError(res, 'Invalid Project document retirement', 400) + const result = await retireLiveProjectFileDocument( + { projectId, fileId, retiredDocId, replacementDocId }, + roomManager.io + ) + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.end(JSON.stringify({ status: result.status })) + } catch (error) { + logger.error('Error retiring Project document:', error) + sendError(res, 'Failed to retire Project document') + } + return + } + if (req.method === 'POST' && req.url === '/api/file-doc/invalidate') { try { const body = await readRequestBody(req) - const { fileId, version } = JSON.parse(body) + const { fileId, version, owner } = JSON.parse(body) if (!isNonEmptyString(fileId)) return sendError(res, 'Invalid fileId', 400) + if ( + owner !== undefined && + (!owner || + owner.entityType !== 'project' || + !isNonEmptyString(owner.entityId) || + /[/:]/.test(owner.entityId) || + /[/:]/.test(fileId)) + ) + return sendError(res, 'Invalid file owner', 400) if (!Number.isSafeInteger(version) || version <= 0) { return sendError(res, 'Invalid version', 400) } - const room = { type: ROOM_TYPES.WORKSPACE_FILE_DOC, id: fileId } as const - const result = await invalidateLiveFileDocument(fileId, version) + const room = owner + ? projectFileDocRoom(owner.entityId, fileId) + : ({ type: ROOM_TYPES.WORKSPACE_FILE_DOC, id: fileId } as const) + const result = await invalidateLiveFileDocument(fileId, version, owner?.entityId) const payload: FileDocInvalidated = { fileId, version, @@ -238,7 +299,7 @@ export function createHttpHandler(roomManager: IRoomManager, logger: Logger) { } if (result.status === 'applied') roomManager.io - .to([roomName(room), fileDocAdmissionRoom(fileId)]) + .to([roomName(room), fileDocAdmissionRoom(fileId, owner?.entityId)]) .emit(FILE_DOC_EVENTS.INVALIDATED, payload) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ status: result.status })) diff --git a/apps/realtime/vitest.config.ts b/apps/realtime/vitest.config.ts index e16fdca4089..539b4220783 100644 --- a/apps/realtime/vitest.config.ts +++ b/apps/realtime/vitest.config.ts @@ -2,27 +2,38 @@ import path from 'node:path' import { defineConfig, mergeConfig } from 'vitest/config' import sharedConfig from '../../vitest.shared' -export default mergeConfig( - sharedConfig, - defineConfig({ - test: { - include: ['**/*.test.{ts,tsx}'], - setupFiles: ['./vitest.setup.ts'], - pool: 'threads', - testTimeout: 10000, - }, - resolve: { - alias: [ - { - find: '@sim/db', - replacement: path.resolve(__dirname, '../../packages/db'), - }, - { - find: '@sim/logger', - replacement: path.resolve(__dirname, '../../packages/logger/src'), - }, - { find: '@', replacement: path.resolve(__dirname, 'src') }, - ], - }, - }) +export default defineConfig(({ mode }) => + mergeConfig( + sharedConfig, + defineConfig({ + test: { + include: mode === 'integration' ? ['src/**/*.integration.ts'] : ['**/*.test.{ts,tsx}'], + setupFiles: [ + mode === 'integration' ? './vitest.integration.setup.ts' : './vitest.setup.ts', + ], + pool: 'threads', + testTimeout: mode === 'integration' ? 30000 : 10000, + ...(mode === 'integration' + ? { + fileParallelism: false, + reporters: ['default', 'json'], + outputFile: { json: 'test-results/integration.json' }, + } + : {}), + }, + resolve: { + alias: [ + { + find: '@sim/db', + replacement: path.resolve(__dirname, '../../packages/db'), + }, + { + find: '@sim/logger', + replacement: path.resolve(__dirname, '../../packages/logger/src'), + }, + { find: '@', replacement: path.resolve(__dirname, 'src') }, + ], + }, + }) + ) ) diff --git a/apps/realtime/vitest.integration.setup.ts b/apps/realtime/vitest.integration.setup.ts new file mode 100644 index 00000000000..84a18b5d327 --- /dev/null +++ b/apps/realtime/vitest.integration.setup.ts @@ -0,0 +1,14 @@ +import { readTestDatabaseUrl, readTestRedisUrl } from '@sim/db/testing/test-infrastructure' + +const redisUrl = readTestRedisUrl() +if (!redisUrl) throw new Error('Realtime integration tests require disposable TEST_REDIS_URL') + +Object.assign(process.env, { + NODE_ENV: 'test', + DATABASE_URL: readTestDatabaseUrl(), + REDIS_URL: redisUrl, + BETTER_AUTH_URL: 'http://localhost:3000', + NEXT_PUBLIC_APP_URL: 'http://localhost:3000', + BETTER_AUTH_SECRET: 'isolated-realtime-fixture-secret-not-a-real-credential', + INTERNAL_API_SECRET: 'isolated-realtime-fixture-internal-secret', +}) diff --git a/apps/sim/app/api/files/authorization.ts b/apps/sim/app/api/files/authorization.ts index ab28084e50c..7c47bc9375e 100644 --- a/apps/sim/app/api/files/authorization.ts +++ b/apps/sim/app/api/files/authorization.ts @@ -59,7 +59,7 @@ function workspacePermissionSatisfies( async function lookupWorkspaceFileByKey( key: string, options?: { includeDeleted?: boolean } -): Promise<{ workspaceId: string; uploadedBy: string } | null> { +): Promise<{ workspaceId: string } | null> { try { const { includeDeleted = false } = options ?? {} // Priority 1: Check new workspaceFiles table @@ -69,7 +69,6 @@ async function lookupWorkspaceFileByKey( if (fileRecord) { return { workspaceId: fileRecord.workspaceId || '', - uploadedBy: fileRecord.userId, } } @@ -78,7 +77,6 @@ async function lookupWorkspaceFileByKey( const [legacyFile] = await db .select({ workspaceId: workspaceFile.workspaceId, - uploadedBy: workspaceFile.uploadedBy, }) .from(workspaceFile) .where( @@ -91,7 +89,6 @@ async function lookupWorkspaceFileByKey( if (legacyFile) { return { workspaceId: legacyFile.workspaceId, - uploadedBy: legacyFile.uploadedBy, } } } catch (legacyError) { @@ -122,9 +119,17 @@ export async function verifyFileAccess( options?: { requireWrite?: boolean; knowledgeAccess?: KnowledgeFileAccess } ): Promise { /** Organization images require the Principal-aware Assistant application resolver. */ - if (cloudKey.startsWith('assistant/') || cloudKey.startsWith('chat-images/')) return false + if ( + cloudKey.startsWith('assistant/') || + cloudKey.startsWith('chat-images/') || + cloudKey.startsWith('project/') || + context === 'project' + ) + return false const requireWrite = options?.requireWrite ?? false try { + const canonical = await getFileMetadataByKey(cloudKey, undefined, { includeDeleted: true }) + if (canonical?.entityType === 'project' || canonical?.context === 'project') return false const keyContext = inferContextFromKey(cloudKey) /** Organization logos are changed only through the organization-authorized upload lifecycle. */ if (keyContext === 'organization-logos') return !requireWrite diff --git a/apps/sim/app/api/files/public/[token]/content/route.test.ts b/apps/sim/app/api/files/public/[token]/content/route.test.ts index ca7640e37c5..c80e4688efe 100644 --- a/apps/sim/app/api/files/public/[token]/content/route.test.ts +++ b/apps/sim/app/api/files/public/[token]/content/route.test.ts @@ -26,7 +26,7 @@ vi.mock('@/lib/core/security/deployment-auth', () => ({ vi.mock('@/lib/uploads/core/storage-service', () => storageServiceMock) -vi.mock('@/lib/mothership/tools/server/files/doc-compile', () => ({ +vi.mock('@/lib/uploads/documents/compile', () => ({ resolveServableDoc: mockResolveServableDoc, })) diff --git a/apps/sim/app/api/files/public/[token]/content/route.ts b/apps/sim/app/api/files/public/[token]/content/route.ts index d8fc088918e..6d7fa2648a4 100644 --- a/apps/sim/app/api/files/public/[token]/content/route.ts +++ b/apps/sim/app/api/files/public/[token]/content/route.ts @@ -8,10 +8,10 @@ import { validateDeploymentAuth } from '@/lib/core/security/deployment-auth' import { generateRequestId } from '@/lib/core/utils/request' import { assertKnownSizeWithinLimit } from '@/lib/core/utils/stream-limits' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' -import { resolveServableDoc } from '@/lib/mothership/tools/server/files/doc-compile' import { enforcePublicFileRateLimit } from '@/lib/public-shares/rate-limit' import { resolveActiveShareByToken } from '@/lib/public-shares/share-manager' import { downloadFile } from '@/lib/uploads/core/storage-service' +import { resolveServableDoc } from '@/lib/uploads/documents/compile' import { resolveServableImageBytes } from '@/lib/uploads/server/image-derivative' import { MAX_BUFFERED_TRANSFER_BYTES } from '@/lib/uploads/shared/types' import { isSimPageSource, SIM_PAGE_CONTENT_TYPE } from '@/lib/workspace-files/page-compile' diff --git a/apps/sim/app/api/files/serve/[...path]/route.test.ts b/apps/sim/app/api/files/serve/[...path]/route.test.ts index 98c539b1afb..b5c7b7234c8 100644 --- a/apps/sim/app/api/files/serve/[...path]/route.test.ts +++ b/apps/sim/app/api/files/serve/[...path]/route.test.ts @@ -108,7 +108,7 @@ vi.mock('@/lib/workspace-files/application/read-workspace-file-content-by-key', readWorkspaceFileContentByKey: { execute: mockReadWorkspaceFileContentByKey }, })) -vi.mock('@/lib/mothership/tools/server/files/doc-compile', () => ({ +vi.mock('@/lib/uploads/documents/compile', () => ({ resolveServableDocBytes: mockResolveServableDocBytes, })) diff --git a/apps/sim/app/api/files/serve/[...path]/route.ts b/apps/sim/app/api/files/serve/[...path]/route.ts index eeff30cd6d7..1e545005156 100644 --- a/apps/sim/app/api/files/serve/[...path]/route.ts +++ b/apps/sim/app/api/files/serve/[...path]/route.ts @@ -13,13 +13,13 @@ import { AuthType, checkSessionOrInternalAuth } from '@/lib/auth/hybrid' import { asOrchestrationError } from '@/lib/core/orchestration/types' import { assertKnownSizeWithinLimit, isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' -import { resolveServableDocBytes } from '@/lib/mothership/tools/server/files/doc-compile' -import { DocCompileUserError } from '@/lib/mothership/tools/server/files/doc-compile-error' import { CopilotFiles, isUsingCloudStorage } from '@/lib/uploads' import type { StorageContext } from '@/lib/uploads/config' import { readOrganizationChatAttachment } from '@/lib/uploads/contexts/organization-assistant/application' import { parseWorkspaceFileKey } from '@/lib/uploads/contexts/workspace/workspace-file-manager' import { downloadFile } from '@/lib/uploads/core/storage-service' +import { resolveServableDocBytes } from '@/lib/uploads/documents/compile' +import { DocCompileUserError } from '@/lib/uploads/documents/compile-error' import { resolveServableImageBytes } from '@/lib/uploads/server/image-derivative' import { resolveStoredFileContext } from '@/lib/uploads/server/metadata' import { MAX_BUFFERED_TRANSFER_BYTES } from '@/lib/uploads/shared/types' diff --git a/apps/sim/app/api/files/uploads/finalizers.ts b/apps/sim/app/api/files/uploads/finalizers.ts index 7e4776d8b8f..4ce5a6cb3cb 100644 --- a/apps/sim/app/api/files/uploads/finalizers.ts +++ b/apps/sim/app/api/files/uploads/finalizers.ts @@ -100,6 +100,8 @@ export async function finalizeUploadPurpose({ authorizeBeforeRegistration, }: FinalizeUploadPurposeParams): Promise { switch (session.purpose) { + case 'project_file': + throw new UploadSessionError('forbidden', 'Project uploads require their Project application') case 'workspace_file': return finalizeInternalWorkspaceFile( session, @@ -141,6 +143,8 @@ export async function loadCompletedUploadPurpose( session: UploadSessionRecord ): Promise { switch (session.purpose) { + case 'project_file': + throw new UploadSessionError('forbidden', 'Project uploads require their Project application') case 'workspace_file': return toV2File(await loadCompletedWorkspaceFileUpload(session)) case 'organization_logo': diff --git a/apps/sim/app/api/folders/[id]/duplicate/route.test.ts b/apps/sim/app/api/folders/[id]/duplicate/route.test.ts index e58b1139441..99866531e6e 100644 --- a/apps/sim/app/api/folders/[id]/duplicate/route.test.ts +++ b/apps/sim/app/api/folders/[id]/duplicate/route.test.ts @@ -65,6 +65,8 @@ function folderRow(overrides: Record = {}) { return { id: SOURCE_FOLDER_ID, resourceType: 'workflow', + entityType: null, + entityId: null, name: 'Source', userId: TEST_USER.id, workspaceId: WORKSPACE_ID, diff --git a/apps/sim/app/api/folders/[id]/duplicate/route.ts b/apps/sim/app/api/folders/[id]/duplicate/route.ts index 84d529d2345..a13ae1464cd 100644 --- a/apps/sim/app/api/folders/[id]/duplicate/route.ts +++ b/apps/sim/app/api/folders/[id]/duplicate/route.ts @@ -17,6 +17,7 @@ import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { deduplicateFolderName } from '@/lib/folders/naming' import { nextFolderSortOrder } from '@/lib/folders/orchestration' import { assertFolderCollectionHasRoom, toFolderApi } from '@/lib/folders/queries' +import { isWorkspaceFolder } from '@/lib/folders/scope' import { folderMutationStatus } from '@/lib/folders/status' import { collectDescendantFolderIds } from '@/lib/folders/subtree' import { duplicateWorkflow } from '@/lib/workflows/persistence/duplicate' @@ -80,7 +81,7 @@ export const POST = withRouteHandler( ) .then((rows) => rows[0]) - if (!sourceFolder) { + if (!sourceFolder || !isWorkspaceFolder(sourceFolder)) { throw new FolderDuplicationError('Source folder not found', 404) } diff --git a/apps/sim/app/api/folders/[id]/route.test.ts b/apps/sim/app/api/folders/[id]/route.test.ts index 80d9f35d5e9..6488bc07424 100644 --- a/apps/sim/app/api/folders/[id]/route.test.ts +++ b/apps/sim/app/api/folders/[id]/route.test.ts @@ -38,6 +38,8 @@ const mockFolder = { name: 'Test Folder', userId: TEST_USER.id, workspaceId: 'workspace-123', + entityType: null, + entityId: null, parentId: null, color: '#6B7280', sortOrder: 1, diff --git a/apps/sim/app/api/folders/[id]/route.ts b/apps/sim/app/api/folders/[id]/route.ts index d436943c2ee..8f37dde27bf 100644 --- a/apps/sim/app/api/folders/[id]/route.ts +++ b/apps/sim/app/api/folders/[id]/route.ts @@ -12,6 +12,7 @@ import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { deleteFolder, updateFolder } from '@/lib/folders/orchestration' import { toFolderApi } from '@/lib/folders/queries' import { folderResourceSupportsLocking } from '@/lib/folders/resource-traits' +import { isWorkspaceFolder } from '@/lib/folders/scope' import { folderMutationStatus } from '@/lib/folders/status' import { captureServerEvent } from '@/lib/posthog/server' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' @@ -63,7 +64,7 @@ export const PUT = withRouteHandler( ) .then((rows) => rows[0]) - if (!existingFolder) { + if (!existingFolder || !isWorkspaceFolder(existingFolder)) { return NextResponse.json({ error: 'Folder not found' }, { status: 404 }) } @@ -167,7 +168,7 @@ export const DELETE = withRouteHandler( .where(and(eq(folderTable.id, id), eq(folderTable.resourceType, resourceType))) .then((rows) => rows[0]) - if (!existingFolder) { + if (!existingFolder || !isWorkspaceFolder(existingFolder)) { return NextResponse.json({ error: 'Folder not found' }, { status: 404 }) } diff --git a/apps/sim/app/api/folders/route.test.ts b/apps/sim/app/api/folders/route.test.ts index 3320db9eafa..f0c1b6ab8a0 100644 --- a/apps/sim/app/api/folders/route.test.ts +++ b/apps/sim/app/api/folders/route.test.ts @@ -82,6 +82,8 @@ describe('Folders API Route', () => { name: 'Test Folder 1', userId: 'user-123', workspaceId: 'workspace-123', + entityType: null, + entityId: null, parentId: null, color: '#6B7280', isExpanded: true, @@ -94,6 +96,8 @@ describe('Folders API Route', () => { name: 'Test Folder 2', userId: 'user-123', workspaceId: 'workspace-123', + entityType: null, + entityId: null, parentId: 'folder-1', color: '#EF4444', isExpanded: false, diff --git a/apps/sim/app/api/v1/admin/folders/[id]/export/route.ts b/apps/sim/app/api/v1/admin/folders/[id]/export/route.ts index 6c3893f16e8..f140b9c5497 100644 --- a/apps/sim/app/api/v1/admin/folders/[id]/export/route.ts +++ b/apps/sim/app/api/v1/admin/folders/[id]/export/route.ts @@ -24,6 +24,7 @@ import { NextResponse } from 'next/server' import { adminV1ExportFolderContract } from '@/lib/api/contracts/v1/admin' import { parseRequest } from '@/lib/api/server' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' +import { isWorkspaceFolder } from '@/lib/folders/scope' import { exportFolderToZip, sanitizePathSegment } from '@/lib/workflows/operations/import-export' import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' import { parseWorkflowVariables } from '@/lib/workflows/variables/parse' @@ -114,12 +115,14 @@ export const GET = withRouteHandler( id: folderTable.id, name: folderTable.name, workspaceId: folderTable.workspaceId, + entityType: folderTable.entityType, + entityId: folderTable.entityId, }) .from(folderTable) .where(and(eq(folderTable.id, folderId), eq(folderTable.resourceType, 'workflow'))) .limit(1) - if (!folderData) { + if (!folderData || !isWorkspaceFolder(folderData)) { return notFoundResponse('Folder') } diff --git a/apps/sim/app/api/v2/files/uploads/utils.ts b/apps/sim/app/api/v2/files/uploads/utils.ts index dad280cfca8..6060184eedc 100644 --- a/apps/sim/app/api/v2/files/uploads/utils.ts +++ b/apps/sim/app/api/v2/files/uploads/utils.ts @@ -1,6 +1,6 @@ import type { V2FileUpload } from '@/lib/api/contracts/v2/files' -import type { V2UploadStatus } from '@/lib/api/contracts/v2/uploads' import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace' +import { presentUploadSessionState } from '@/lib/uploads/upload-session/presenter' import type { UploadSessionRecord } from '@/lib/uploads/upload-session/service' import { toV2File } from '@/app/api/v2/files/utils' @@ -9,29 +9,7 @@ export async function toV2FileUpload( file: WorkspaceFileRecord | null ): Promise { return { - id: session.id, - status: uploadStatus(session.status), - name: session.fileName, - contentType: session.contentType, - size: session.fileSize, - expiresAt: session.expiresAt.toISOString(), - error: session.error, + ...presentUploadSessionState(session), file: file ? await toV2File(file) : null, } } - -function uploadStatus(status: string): V2UploadStatus { - if ( - status !== 'uploading' && - status !== 'completing' && - status !== 'finalizing' && - status !== 'completed' && - status !== 'failed' && - status !== 'aborting' && - status !== 'aborted' && - status !== 'expired' - ) { - throw new Error(`Invalid upload session status: ${status}`) - } - return status -} diff --git a/apps/sim/app/api/v2/uploads/[uploadId]/parts/[partNumber]/route.ts b/apps/sim/app/api/v2/uploads/[uploadId]/parts/[partNumber]/route.ts index 7a3aa46a8a3..6278b87a9cf 100644 --- a/apps/sim/app/api/v2/uploads/[uploadId]/parts/[partNumber]/route.ts +++ b/apps/sim/app/api/v2/uploads/[uploadId]/parts/[partNumber]/route.ts @@ -3,6 +3,7 @@ import { localUploadPartContract } from '@/lib/api/contracts/upload-sessions' import { parseRequest } from '@/lib/api/server' import { V2_PARSE_DEFAULTS } from '@/lib/api/server/routes' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' +import { queueRetiredProjectUploadCleanup } from '@/lib/projects/files/prefix-cleanup' import { LocalUploadBodyError, writeLocalMultipartPart, @@ -88,11 +89,14 @@ export const PUT = withRouteHandler( try { await writeLocalMultipartPart({ uploadId, partNumber, body: request.body, expectedSize }) } catch (error) { + await queueRetiredProjectUploadCleanup(session) if (error instanceof LocalUploadBodyError) { return v2Error('BAD_REQUEST', error.message) } throw error } + if (await queueRetiredProjectUploadCleanup(session)) + return v2Error('CONFLICT', 'The Project no longer exists') return new NextResponse(null, { status: 204 }) }, { diff --git a/apps/sim/app/api/v2/uploads/[uploadId]/route.ts b/apps/sim/app/api/v2/uploads/[uploadId]/route.ts index 58ba867229b..0a2ec6ccebf 100644 --- a/apps/sim/app/api/v2/uploads/[uploadId]/route.ts +++ b/apps/sim/app/api/v2/uploads/[uploadId]/route.ts @@ -3,6 +3,7 @@ import { localPutUploadContract } from '@/lib/api/contracts/upload-sessions' import { parseRequest } from '@/lib/api/server' import { V2_PARSE_DEFAULTS } from '@/lib/api/server/routes' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' +import { queueRetiredProjectUploadCleanup } from '@/lib/projects/files/prefix-cleanup' import { LocalUploadBodyError, writeLocalPutObject } from '@/lib/uploads/upload-session/provider' import { getOwnedUploadSession, @@ -78,11 +79,14 @@ export const PUT = withRouteHandler( metadata: uploadSessionObjectMetadata(session), }) } catch (error) { + await queueRetiredProjectUploadCleanup(session) if (error instanceof LocalUploadBodyError) { return v2Error('BAD_REQUEST', error.message) } throw error } + if (await queueRetiredProjectUploadCleanup(session)) + return v2Error('CONFLICT', 'The Project no longer exists') return new NextResponse(null, { status: 204 }) }, { diff --git a/apps/sim/background/cleanup-file-versions.ts b/apps/sim/background/cleanup-file-versions.ts index 0564806c50d..76f1c321b4c 100644 --- a/apps/sim/background/cleanup-file-versions.ts +++ b/apps/sim/background/cleanup-file-versions.ts @@ -12,6 +12,7 @@ import { DEFAULT_WORKSPACE_CHUNK_SIZE, } from '@/lib/cleanup/batch-delete' import { retentionCleanupQueue } from '@/lib/cleanup/queue' +import { cleanupProjectFileVersions } from '@/lib/projects/files/retention' import { enqueueWorkspaceFileStorageCleanups } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' import { MAX_SUPERSEDED_FILE_VERSIONS } from '@/lib/uploads/contexts/workspace/workspace-file-versions' @@ -145,6 +146,14 @@ function deleteVersions(rows: Array<{ id: string }>): Promise { export async function runCleanupFileVersions(payload: CleanupJobPayload): Promise { const startTime = Date.now() const { workspaceIds, retentionHours, label, plan } = payload + let projectDeleted = 0 + for (const projectId of [...new Set(payload.projectIds ?? [])].sort()) { + if (projectDeleted >= MAX_VERSIONS_PER_RUN) break + projectDeleted += await cleanupProjectFileVersions( + projectId, + MAX_VERSIONS_PER_RUN - projectDeleted + ) + } if (workspaceIds.length === 0) { logger.info(`[${label}] No workspaces to process`) return @@ -157,8 +166,8 @@ export async function runCleanupFileVersions(payload: CleanupJobPayload): Promis `[${label}] Processing ${workspaceIds.length} workspaces, cutoff: ${cutoff.toISOString()}` ) - let deleted = 0 - let attempted = 0 + let deleted = projectDeleted + let attempted = projectDeleted for (const group of chunkArray(workspaceIds, DEFAULT_WORKSPACE_CHUNK_SIZE)) { if (attempted >= MAX_VERSIONS_PER_RUN) break const candidates = await selectCandidateFileIds(group, cutoff, maxSuperseded) diff --git a/apps/sim/background/cleanup-soft-deletes.test.ts b/apps/sim/background/cleanup-soft-deletes.test.ts index b7c1bfeaace..24bcc51b3dd 100644 --- a/apps/sim/background/cleanup-soft-deletes.test.ts +++ b/apps/sim/background/cleanup-soft-deletes.test.ts @@ -491,7 +491,14 @@ describe('folder cleanup target', () => { queueTableRows(schemaMock.workflow, []) queueTableRows(schemaMock.workspaceFiles, []) queueTableRows(schemaMock.folder, [ - { id: 'sub-1', name: 'Reports', workspaceId: 'ws-1', resourceType: 'knowledge_base' }, + { + id: 'sub-1', + name: 'Reports', + workspaceId: 'ws-1', + resourceType: 'knowledge_base', + entityType: null, + entityId: null, + }, ]) mockDeduplicateFolderName.mockResolvedValueOnce('Reports (1)') diff --git a/apps/sim/background/cleanup-soft-deletes.ts b/apps/sim/background/cleanup-soft-deletes.ts index ba955e7e2ab..412ebabfa00 100644 --- a/apps/sim/background/cleanup-soft-deletes.ts +++ b/apps/sim/background/cleanup-soft-deletes.ts @@ -40,8 +40,14 @@ import { resolveCleanupOwnerScope, } from '@/lib/cleanup/resource-scope' import { deduplicateFolderName } from '@/lib/folders/naming' +import { requireWorkspaceFolder } from '@/lib/folders/scope' import { settleDetachedConnectorReservations } from '@/lib/knowledge/connectors/detachment' import { hardDeleteDocuments } from '@/lib/knowledge/documents/service' +import { + cleanupArchivedProjectFileFolders, + cleanupArchivedProjectFiles, +} from '@/lib/projects/files/retention' +import { lockWorkspaceProject } from '@/lib/projects/membership' import type { StorageContext } from '@/lib/uploads' import { isUsingCloudStorage, StorageService } from '@/lib/uploads' import { allocateUniqueWorkspaceFileName } from '@/lib/uploads/contexts/workspace/workspace-file-manager' @@ -334,6 +340,7 @@ async function deleteExpiredBillableWorkspaceFileRows( for (const batch of chunkArray(workspaceRows, DEFAULT_DELETE_CHUNK_SIZE)) { try { const deletedCount = await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, workspaceId) await releaseWorkspaceFileVersionsForPurgeInTx( tx, batch.map(({ id }) => id), @@ -653,11 +660,14 @@ async function reRootActiveFolderChildrenUnguarded( name: folderTable.name, workspaceId: folderTable.workspaceId, resourceType: folderTable.resourceType, + entityType: folderTable.entityType, + entityId: folderTable.entityId, }) .from(folderTable) .where(and(inArray(folderTable.parentId, expiredIds), isNull(folderTable.deletedAt))) - for (const row of childFolders) { + for (const childFolder of childFolders) { + const row = requireWorkspaceFolder(childFolder) await reRootOne( async () => { const name = await deduplicateFolderName( @@ -840,6 +850,10 @@ export async function runCleanupSoftDeletes( const startTime = Date.now() const { workspaceIds, retentionHours, label } = payload const scope = resolveCleanupOwnerScope(payload) + for (const projectId of [...new Set(payload.projectIds ?? [])].sort()) { + await cleanupArchivedProjectFiles(projectId, budgets?.files) + await cleanupArchivedProjectFileFolders(projectId, budgets?.folders) + } if (scope.ids.length === 0) { logger.info(`[${label}] No resource owners to process`) diff --git a/apps/sim/lib/billing/cleanup-dispatcher.test.ts b/apps/sim/lib/billing/cleanup-dispatcher.test.ts index 3c296112f9b..cd106b647cd 100644 --- a/apps/sim/lib/billing/cleanup-dispatcher.test.ts +++ b/apps/sim/lib/billing/cleanup-dispatcher.test.ts @@ -91,6 +91,7 @@ describe('organization-owned Search retention dispatch', () => { mockGetOrganizationSubscription.mockResolvedValue(null) mockEnqueue.mockResolvedValue('cleanup-job') queueTableRows(schemaMock.workspace, []) + queueTableRows(schemaMock.project, []) }) it('dispatches an organization with zero workspaces using its own configured window', async () => { diff --git a/apps/sim/lib/billing/cleanup-dispatcher.ts b/apps/sim/lib/billing/cleanup-dispatcher.ts index 96cb7c8a639..1577e633acb 100644 --- a/apps/sim/lib/billing/cleanup-dispatcher.ts +++ b/apps/sim/lib/billing/cleanup-dispatcher.ts @@ -1,6 +1,6 @@ import { db } from '@sim/db' import type { DataRetentionSettings, WorkspaceMode } from '@sim/db/schema' -import { organization, workspace } from '@sim/db/schema' +import { organization, project, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { chunkArray } from '@sim/utils/helpers' import { tasks } from '@trigger.dev/sdk' @@ -10,6 +10,7 @@ import { getOrganizationSubscription } from '@/lib/billing/core/billing' import { getHighestPriorityPersonalSubscription } from '@/lib/billing/core/subscription' import { getPlanType, type PlanCategory } from '@/lib/billing/plan-helpers' import { type RetentionHoursKey, resolveEffectiveRetentionHours } from '@/lib/billing/retention' +import { resolveProjectStorageBillingContext } from '@/lib/billing/storage/context' import { type CleanupBudgets, type CleanupLimits, createCleanupBudgets } from '@/lib/cleanup/limits' import { getJobQueue } from '@/lib/core/async-jobs' import { shouldExecuteInline } from '@/lib/core/async-jobs/config' @@ -43,6 +44,8 @@ export interface CleanupJobPayload { workspaceIds: string[] /** Organization-owned Search data is retained independently of workspace membership. */ organizationIds?: string[] + /** Shared file retention follows the canonical Project payer. */ + projectIds?: string[] retentionHours: number label: string /** Set on exactly one chunk per dispatch so plan-wide housekeeping runs once. */ @@ -323,6 +326,72 @@ async function forEachCleanupChunk( } } + if (jobType === 'cleanup-soft-deletes' || jobType === 'cleanup-file-versions') { + let afterProjectId: string | null = null + while (!shouldStop()) { + const rows = await db + .select({ + id: project.id, + ownerId: project.ownerId, + organizationId: project.organizationId, + settings: organization.dataRetentionSettings, + }) + .from(project) + .leftJoin(organization, eq(organization.id, project.organizationId)) + .where( + and( + isNull(project.archivedAt), + afterProjectId ? gt(project.id, afterProjectId) : undefined + ) + ) + .orderBy(asc(project.id)) + .limit(pageSize) + if (!rows.length) break + afterProjectId = rows[rows.length - 1].id + const groups = new Map< + string, + { plan: PlanCategory; retentionHours: number; projectIds: string[] } + >() + for (const row of rows) { + if (shouldStop()) break + try { + let plan: PlanCategory = 'enterprise' + if (isBillingEnabled) { + const billing = await resolveProjectStorageBillingContext({ + projectId: row.id, + ownerId: row.ownerId, + organizationId: row.organizationId, + }) + if (row.organizationId && billing.plan === null) continue + plan = getPlanType(billing.plan) + } + const retentionHours = + plan === 'enterprise' ? (row.settings?.[config.key] ?? null) : config.defaults[plan] + if (retentionHours === null) continue + if (!Number.isFinite(retentionHours) || retentionHours < 0) + throw new Error('Invalid Project retention policy') + const key = `${plan}:${retentionHours}` + const group = groups.get(key) + if (group) group.projectIds.push(row.id) + else groups.set(key, { plan, retentionHours, projectIds: [row.id] }) + } catch (error) { + if (failOnLookupError) throw error + logger.error('Skipping Project cleanup after payer policy lookup failed', { + projectId: row.id, + error, + }) + } + } + for (const group of groups.values()) { + await emitChunk({ + ...group, + workspaceIds: [], + label: `${group.plan}/projects/${group.projectIds[0]}`, + }) + } + } + } + if (jobType === 'cleanup-soft-deletes' || jobType === 'cleanup-tasks') { let afterOrganizationId: string | null = null while (!shouldStop()) { diff --git a/apps/sim/lib/billing/core/billing-attribution.ts b/apps/sim/lib/billing/core/billing-attribution.ts index 9e734dc487c..0ed9a8da747 100644 --- a/apps/sim/lib/billing/core/billing-attribution.ts +++ b/apps/sim/lib/billing/core/billing-attribution.ts @@ -24,6 +24,7 @@ import { parseWorkflowExecutionTimeoutSeconds } from '@/lib/billing/execution-ti import { isEnterprise } from '@/lib/billing/plan-helpers' import { isBillingEnabled, isHosted } from '@/lib/core/config/env-flags' import { type ResourceOwner, resourceScopeFromOwner } from '@/lib/core/resource-scope' +import type { DbOrTx } from '@/lib/db/types' import { BILLING_ACCOUNT_DECISION_HEADER, BILLING_ACCOUNT_DECISION_HEADER_MAX_BYTES, @@ -113,6 +114,7 @@ export interface ResolveBillingAttributionParams { } export interface ResolveWorkspaceBillingPayerOptions { + executor?: DbOrTx onMissing?: 'throw' | 'return-null' } @@ -674,7 +676,8 @@ export async function resolveWorkspaceBillingPayer( workspaceId: string, options: ResolveWorkspaceBillingPayerOptions = {} ) { - const [workspacePayer] = await db + const executor = options.executor ?? db + const [workspacePayer] = await executor .select({ billedAccountUserId: workspace.billedAccountUserId, organizationId: workspace.organizationId, @@ -690,8 +693,11 @@ export async function resolveWorkspaceBillingPayer( const { billedAccountUserId, organizationId } = workspacePayer const payerSubscription = organizationId - ? await getOrganizationSubscription(organizationId, { onError: 'throw' }) - : await getHighestPriorityPersonalSubscription(billedAccountUserId, { onError: 'throw' }) + ? await getOrganizationSubscription(organizationId, { onError: 'throw', executor }) + : await getHighestPriorityPersonalSubscription(billedAccountUserId, { + onError: 'throw', + executor, + }) const expectedReferenceId = organizationId ?? billedAccountUserId if (payerSubscription && payerSubscription.referenceId !== expectedReferenceId) { diff --git a/apps/sim/lib/billing/core/limit-notifications.ts b/apps/sim/lib/billing/core/limit-notifications.ts index 8a4311e2811..15c8a2c7257 100644 --- a/apps/sim/lib/billing/core/limit-notifications.ts +++ b/apps/sim/lib/billing/core/limit-notifications.ts @@ -11,6 +11,7 @@ import { buildUpgradeHref, type UpgradeReason } from '@/lib/billing/upgrade-reas import { isBillingEnabled } from '@/lib/core/config/env-flags' import { getBaseUrl } from '@/lib/core/utils/urls' import { getEmailPreferences } from '@/lib/messaging/email/unsubscribe' +import { organizationRoutes } from '@/lib/navigation/paths' const logger = createLogger('LimitNotifications') @@ -246,7 +247,7 @@ export async function resolveLimitEmailRecipients( export async function maybeSendLimitThresholdEmail(params: { category: LimitCategory scope: 'user' | 'organization' - workspaceId: string + workspaceId?: string currentUsage: number limit: number /** Pre-formatted current usage for the email body, e.g. "4.2 GB", "9 seats". */ @@ -288,7 +289,12 @@ export async function maybeSendLimitThresholdEmail(params: { const kind = desired === REACH_THRESHOLD ? 'reached' : 'warning' const percentUsed = Math.min(100, Math.round(percent)) - const upgradeLink = `${getBaseUrl()}${buildUpgradeHref(params.workspaceId, category)}` + const billingPath = params.workspaceId + ? buildUpgradeHref(params.workspaceId, category) + : scope === 'organization' && params.organizationId + ? organizationRoutes(params.organizationId).settingsSection('billing') + : '/account/settings/billing' + const upgradeLink = `${getBaseUrl()}${billingPath}` const [{ getLimitEmailSubject, renderLimitThresholdEmail }, { sendEmail }] = await Promise.all([ import('@/components/emails'), @@ -348,12 +354,12 @@ export async function maybeSendLimitThresholdEmail(params: { * (the uploader for storage; the workspace's billed account for tables). * @param params.subscription - Pre-resolved subscription (may be `null`) to skip * the `getHighestPrioritySubscription` lookup on hot paths; omit to fetch here. - * @param params.billingEntity - Exact workspace payer when already resolved. + * @param params.billingEntity - Exact resource payer when already resolved. */ export async function maybeNotifyLimit(params: { category: LimitCategory billedUserId: string - workspaceId: string + workspaceId?: string currentUsage: number limit: number usageLabel: string diff --git a/apps/sim/lib/billing/organizations/lock-order.test.ts b/apps/sim/lib/billing/organizations/lock-order.test.ts index 108bb594f63..e8e732762c8 100644 --- a/apps/sim/lib/billing/organizations/lock-order.test.ts +++ b/apps/sim/lib/billing/organizations/lock-order.test.ts @@ -17,20 +17,19 @@ import { workspace, } from '@sim/db/schema' import { dbChainMockFns, resetDbChainMock } from '@sim/testing' +import { + billingPayerTransferMock, + billingPayerTransferMockFns, +} from '@sim/testing/mocks/billing-payer-transfer.mock' import { outboxServiceMock } from '@sim/testing/mocks/outbox-service.mock' import { beforeEach, describe, expect, it, vi } from 'vitest' -const { mockChangeOrganizationWorkspaceBilledAccountsInTx, mockChangeWorkspaceStoragePayersInTx } = - vi.hoisted(() => ({ - mockChangeOrganizationWorkspaceBilledAccountsInTx: vi.fn(), - mockChangeWorkspaceStoragePayersInTx: vi.fn(), - })) +const { + mockChangeOrganizationWorkspaceBilledAccountsInTx, + mockChangeProjectAndWorkspaceStoragePayersInTx, +} = billingPayerTransferMockFns -vi.mock('@/lib/billing/storage/payer-transfer', () => ({ - changeOrganizationWorkspaceBilledAccountsInTx: mockChangeOrganizationWorkspaceBilledAccountsInTx, - changeWorkspaceStoragePayerInTx: vi.fn(), - changeWorkspaceStoragePayersInTx: mockChangeWorkspaceStoragePayersInTx, -})) +vi.mock('@/lib/billing/storage/payer-transfer', () => billingPayerTransferMock) import { reapplyPaidOrgJoinBillingForExistingMemberTx, @@ -102,7 +101,7 @@ describe('paid-org join billing lock ordering', () => { beforeEach(() => { resetDbChainMock() mockChangeOrganizationWorkspaceBilledAccountsInTx.mockReset() - mockChangeWorkspaceStoragePayersInTx.mockReset() + mockChangeProjectAndWorkspaceStoragePayersInTx.mockReset() }) it('locks the personal subscription before pausing it and never mutates userStats', async () => { @@ -250,7 +249,7 @@ describe('workspace payer-change transaction lock ordering', () => { }), }), } - mockChangeWorkspaceStoragePayersInTx.mockImplementationOnce(async () => { + mockChangeProjectAndWorkspaceStoragePayersInTx.mockImplementationOnce(async () => { ops.push({ op: 'payer-transfer', table: workspace }) return [] }) diff --git a/apps/sim/lib/billing/organizations/pause-pro-for-coverage.test.ts b/apps/sim/lib/billing/organizations/pause-pro-for-coverage.test.ts index 8354ebd0d07..08729f815f5 100644 --- a/apps/sim/lib/billing/organizations/pause-pro-for-coverage.test.ts +++ b/apps/sim/lib/billing/organizations/pause-pro-for-coverage.test.ts @@ -1,12 +1,9 @@ import { dbChainMockFns, resetDbChainMock } from '@sim/testing' +import { billingPayerTransferMock } from '@sim/testing/mocks/billing-payer-transfer.mock' import { outboxServiceMock, outboxServiceMockFns } from '@sim/testing/mocks/outbox-service.mock' import { beforeEach, describe, expect, it, vi } from 'vitest' -vi.mock('@/lib/billing/storage/payer-transfer', () => ({ - changeOrganizationWorkspaceBilledAccountsInTx: vi.fn(), - changeWorkspaceStoragePayerInTx: vi.fn(), - changeWorkspaceStoragePayersInTx: vi.fn(), -})) +vi.mock('@/lib/billing/storage/payer-transfer', () => billingPayerTransferMock) vi.mock('@/lib/core/outbox/service', () => outboxServiceMock) import { pauseProSubscriptionForOrgCoverage } from '@/lib/billing/organizations/membership' diff --git a/apps/sim/lib/billing/storage/context.ts b/apps/sim/lib/billing/storage/context.ts index f6dc7b95cc8..b42986fc53f 100644 --- a/apps/sim/lib/billing/storage/context.ts +++ b/apps/sim/lib/billing/storage/context.ts @@ -1,19 +1,40 @@ +import { db } from '@sim/db' +import { member } from '@sim/db/schema' import { isRecordLike } from '@sim/utils/object' +import { and, eq } from 'drizzle-orm' +import { getOrganizationSubscription } from '@/lib/billing/core/billing' import { resolveWorkspaceBillingPayer } from '@/lib/billing/core/billing-attribution' +import { getHighestPriorityPersonalSubscription } from '@/lib/billing/core/subscription' import type { BillingEntity } from '@/lib/billing/core/usage-log' +import type { DbOrTx } from '@/lib/db/types' /** - * Immutable workspace-selected payer used for every storage quota and counter + * Immutable resource-selected payer used for every storage quota and counter * decision in one operation. Actor identity is deliberately absent. */ -export interface StorageBillingContext { - readonly workspaceId: string +export interface StoragePayerContext { readonly billedAccountUserId: string readonly billingEntity: Readonly readonly plan: string | null readonly customStorageLimitGB: number | null } +export interface StorageBillingContext extends StoragePayerContext { + readonly workspaceId: string +} + +/** Canonical Project ownership supplied by an authorized application operation. */ +export interface ProjectStorageOwnerSnapshot { + readonly projectId: string + readonly ownerId: string + readonly organizationId: string | null +} + +/** Ownership and payer assertions are rechecked under the Project mutation lock. */ +export interface ProjectStorageBillingContext + extends StoragePayerContext, + ProjectStorageOwnerSnapshot {} + function readCustomStorageLimitGB(metadata: unknown): number | null { if (!isRecordLike(metadata)) return null const value = metadata.customStorageLimitGB @@ -25,9 +46,10 @@ function readCustomStorageLimitGB(metadata: unknown): number | null { * the uploader's subscriptions or organization memberships. */ export async function resolveStorageBillingContext( - workspaceId: string + workspaceId: string, + executor: DbOrTx = db ): Promise { - const payer = await resolveWorkspaceBillingPayer(workspaceId) + const payer = await resolveWorkspaceBillingPayer(workspaceId, { executor }) if (!payer) { throw new Error(`Unable to resolve storage payer for workspace ${workspaceId}`) } @@ -48,3 +70,43 @@ export async function resolveStorageBillingContext( : null, }) } + +/** Resolves a Project's exact organization or personal payer independently of its creator and caller. */ +export async function resolveProjectStorageBillingContext( + owner: ProjectStorageOwnerSnapshot, + executor: DbOrTx = db +): Promise { + const [organizationOwners, payerSubscription] = await Promise.all([ + owner.organizationId + ? executor + .select({ userId: member.userId }) + .from(member) + .where(and(eq(member.organizationId, owner.organizationId), eq(member.role, 'owner'))) + .limit(1) + : Promise.resolve([]), + owner.organizationId + ? getOrganizationSubscription(owner.organizationId, { onError: 'throw', executor }) + : getHighestPriorityPersonalSubscription(owner.ownerId, { + onError: 'throw', + executor, + }), + ]) + const billedAccountUserId = owner.organizationId ? organizationOwners[0]?.userId : owner.ownerId + if (!billedAccountUserId) throw new Error('Organization billing owner is unavailable') + const payerId = owner.organizationId ?? owner.ownerId + if (payerSubscription && payerSubscription.referenceId !== payerId) { + throw new Error(`Resolved subscription does not belong to Project payer ${payerId}`) + } + const billingEntity: BillingEntity = owner.organizationId + ? { type: 'organization', id: owner.organizationId } + : { type: 'user', id: owner.ownerId } + return Object.freeze({ + ...owner, + billedAccountUserId, + billingEntity: Object.freeze(billingEntity), + plan: payerSubscription?.plan ?? null, + customStorageLimitGB: owner.organizationId + ? readCustomStorageLimitGB(payerSubscription?.metadata) + : null, + }) +} diff --git a/apps/sim/lib/billing/storage/index.ts b/apps/sim/lib/billing/storage/index.ts index 4be6f5cd100..790eb6a97fa 100644 --- a/apps/sim/lib/billing/storage/index.ts +++ b/apps/sim/lib/billing/storage/index.ts @@ -1,4 +1,7 @@ -export { resolveStorageBillingContext, type StorageBillingContext } from './context' +export { + resolveStorageBillingContext, + type StorageBillingContext, +} from './context' export { checkStorageQuota, checkStorageQuotaForBillingContext, diff --git a/apps/sim/lib/billing/storage/limits.ts b/apps/sim/lib/billing/storage/limits.ts index 96d27a82265..d3f5433d7cb 100644 --- a/apps/sim/lib/billing/storage/limits.ts +++ b/apps/sim/lib/billing/storage/limits.ts @@ -17,7 +17,7 @@ import { eq } from 'drizzle-orm' import type { HighestPrioritySubscription } from '@/lib/billing/core/plan' import type { BillingEntity } from '@/lib/billing/core/usage-log' import { getPlanTypeForLimits, isEnterprise, isFree } from '@/lib/billing/plan-helpers' -import type { StorageBillingContext } from '@/lib/billing/storage/context' +import type { StoragePayerContext } from '@/lib/billing/storage/context' import { getLegacyStorageBillingEntity } from '@/lib/billing/storage/entity' import { getEnv } from '@/lib/core/config/env' import { isBillingEnabled } from '@/lib/core/config/env-flags' @@ -219,7 +219,7 @@ export function getStorageLimits() { * Gets the storage limit for an immutable workspace payer without resolving an * actor subscription. */ -export function getStorageLimitForBillingContext(context: StorageBillingContext): number { +export function getStorageLimitForBillingContext(context: StoragePayerContext): number { return resolveStorageLimit({ plan: context.plan, scope: context.billingEntity.type, @@ -294,7 +294,7 @@ export async function getUserStorageUsage( * Reads the exact workspace payer's storage counter. */ export async function getStorageUsageForBillingContext( - context: StorageBillingContext + context: StoragePayerContext ): Promise { try { return await readStorageUsageForEntity(context.billingEntity) @@ -320,7 +320,7 @@ async function resolveUserStorageQuota(userId: string): Promise { return { currentUsage: await readStorageUsageForEntity(context.billingEntity), @@ -368,7 +368,7 @@ export async function checkStorageQuota( * Checks storage quota against a workspace-selected immutable payer. */ export async function checkStorageQuotaForBillingContext( - context: StorageBillingContext, + context: StoragePayerContext, additionalBytes: number ): Promise { return checkStorageQuotaWithResolver( diff --git a/apps/sim/lib/billing/storage/payer-transfer.ts b/apps/sim/lib/billing/storage/payer-transfer.ts index e3f15617413..94a2dfc41fb 100644 --- a/apps/sim/lib/billing/storage/payer-transfer.ts +++ b/apps/sim/lib/billing/storage/payer-transfer.ts @@ -3,6 +3,7 @@ import { knowledgeBase, knowledgeConnector, organization, + project, userStats, workspace, workspaceFiles, @@ -10,7 +11,9 @@ import { import { createLogger } from '@sim/logger' import { and, asc, eq, inArray, sql } from 'drizzle-orm' import type { BillingEntity } from '@/lib/billing/core/usage-log' -import type { DbOrTx } from '@/lib/db/types' +import type { ProjectStorageOwnerSnapshot } from '@/lib/billing/storage/context' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' +import { lockProject } from '@/lib/projects/membership' const logger = createLogger('WorkspaceStoragePayerTransfer') @@ -42,6 +45,23 @@ export interface ChangeWorkspaceStoragePayerResult { repairedWorkspaceLedger: boolean } +export interface ChangeProjectStoragePayerParams extends ProjectStorageOwnerSnapshot { + expectedCurrentOwner: Pick +} + +interface ChangeProjectStoragePayerResult { + projectId: string + billableBytes: number + oldPayer: BillingEntity + newPayer: BillingEntity +} + +interface RemovedProjectStorageContribution { + projectId: string + billableBytes: number + payer: BillingEntity +} + interface PayerStorageDelta { incomingBytes: number outgoingBytes: number @@ -375,6 +395,14 @@ async function setStoragePayerUsagesBatch( export async function changeWorkspaceStoragePayersInTx( tx: DbOrTx, changes: ChangeWorkspaceStoragePayerParams[] +): Promise { + return changeWorkspaceStoragePayers(tx, changes, false) +} + +async function changeWorkspaceStoragePayers( + tx: DbOrTx, + changes: ChangeWorkspaceStoragePayerParams[], + deferPayerUpdates: boolean ): Promise { if (changes.length === 0) return [] @@ -432,76 +460,81 @@ export async function changeWorkspaceStoragePayersInTx( tx, payerChangingWorkspaceIds ) - const payerUsageByKey = await lockStoragePayers(tx, payerByKey) - const deltaByPayerKey = new Map() - - for (const workspaceId of payerChangingWorkspaceIds) { - const lockedWorkspace = workspaceById.get(workspaceId) - const change = changesByWorkspaceId.get(workspaceId) - if (!lockedWorkspace || !change) continue - const exactBytes = exactBytesByWorkspaceId.get(workspaceId) ?? 0 - const oldPayer = getWorkspacePayer(lockedWorkspace) - const newPayer = getWorkspacePayer(change) - const oldPayerKey = getPayerKey(oldPayer) - const newPayerKey = getPayerKey(newPayer) - - if (payerUsageByKey.get(newPayerKey) === null) { - throw new Error(`Storage destination payer ${newPayerKey} not found`) - } - - const sourceDelta = deltaByPayerKey.get(oldPayerKey) ?? { - incomingBytes: 0, - outgoingBytes: 0, - } - sourceDelta.outgoingBytes += exactBytes - if (!Number.isSafeInteger(sourceDelta.outgoingBytes)) { - throw new Error(`Storage source payer ${oldPayerKey} delta exceeds the safe integer range`) - } - deltaByPayerKey.set(oldPayerKey, sourceDelta) - - const destinationDelta = deltaByPayerKey.get(newPayerKey) ?? { - incomingBytes: 0, - outgoingBytes: 0, - } - destinationDelta.incomingBytes += exactBytes - if (!Number.isSafeInteger(destinationDelta.incomingBytes)) { - throw new Error( - `Storage destination payer ${newPayerKey} delta exceeds the safe integer range` - ) + if (!deferPayerUpdates) { + const payerUsageByKey = await lockStoragePayers(tx, payerByKey) + const deltaByPayerKey = new Map() + + for (const workspaceId of payerChangingWorkspaceIds) { + const lockedWorkspace = workspaceById.get(workspaceId) + const change = changesByWorkspaceId.get(workspaceId) + if (!lockedWorkspace || !change) continue + const exactBytes = exactBytesByWorkspaceId.get(workspaceId) ?? 0 + const oldPayer = getWorkspacePayer(lockedWorkspace) + const newPayer = getWorkspacePayer(change) + const oldPayerKey = getPayerKey(oldPayer) + const newPayerKey = getPayerKey(newPayer) + + if (payerUsageByKey.get(newPayerKey) === null) { + throw new Error(`Storage destination payer ${newPayerKey} not found`) + } + + const sourceDelta = deltaByPayerKey.get(oldPayerKey) ?? { + incomingBytes: 0, + outgoingBytes: 0, + } + sourceDelta.outgoingBytes += exactBytes + if (!Number.isSafeInteger(sourceDelta.outgoingBytes)) { + throw new Error(`Storage source payer ${oldPayerKey} delta exceeds the safe integer range`) + } + deltaByPayerKey.set(oldPayerKey, sourceDelta) + + const destinationDelta = deltaByPayerKey.get(newPayerKey) ?? { + incomingBytes: 0, + outgoingBytes: 0, + } + destinationDelta.incomingBytes += exactBytes + if (!Number.isSafeInteger(destinationDelta.incomingBytes)) { + throw new Error( + `Storage destination payer ${newPayerKey} delta exceeds the safe integer range` + ) + } + deltaByPayerKey.set(newPayerKey, destinationDelta) } - deltaByPayerKey.set(newPayerKey, destinationDelta) - } - const nextUsageByKey = new Map() - for (const [payerKey, delta] of [...deltaByPayerKey.entries()].sort(([left], [right]) => - comparePayerKeys(left, right) - )) { - const currentUsage = payerUsageByKey.get(payerKey) - if (currentUsage === null || currentUsage === undefined) { - logger.warn('Storage source payer is missing during workspace payer batch change', { - sourcePayer: payerKey, - outgoingBytes: delta.outgoingBytes, - }) - continue + const nextUsageByKey = new Map() + for (const [payerKey, delta] of [...deltaByPayerKey.entries()].sort(([left], [right]) => + comparePayerKeys(left, right) + )) { + const currentUsage = payerUsageByKey.get(payerKey) + if (currentUsage === null || currentUsage === undefined) { + logger.warn('Storage source payer is missing during workspace payer batch change', { + sourcePayer: payerKey, + outgoingBytes: delta.outgoingBytes, + }) + continue + } + + const usageAfterOutgoing = Math.max(0, currentUsage - delta.outgoingBytes) + if (currentUsage < delta.outgoingBytes) { + logger.warn( + 'Clamping drifted source storage aggregate during workspace payer batch change', + { + sourcePayer: payerKey, + sourceUsage: currentUsage, + outgoingBytes: delta.outgoingBytes, + } + ) + } + const nextUsage = usageAfterOutgoing + delta.incomingBytes + if (!Number.isSafeInteger(nextUsage)) { + throw new Error(`Storage payer ${payerKey} exceeds the safe integer range`) + } + nextUsageByKey.set(payerKey, nextUsage) } - const usageAfterOutgoing = Math.max(0, currentUsage - delta.outgoingBytes) - if (currentUsage < delta.outgoingBytes) { - logger.warn('Clamping drifted source storage aggregate during workspace payer batch change', { - sourcePayer: payerKey, - sourceUsage: currentUsage, - outgoingBytes: delta.outgoingBytes, - }) - } - const nextUsage = usageAfterOutgoing + delta.incomingBytes - if (!Number.isSafeInteger(nextUsage)) { - throw new Error(`Storage payer ${payerKey} exceeds the safe integer range`) - } - nextUsageByKey.set(payerKey, nextUsage) + await setStoragePayerUsagesBatch(tx, payerByKey, nextUsageByKey) } - await setStoragePayerUsagesBatch(tx, payerByKey, nextUsageByKey) - const resultsByWorkspaceId = new Map() const workspaceUpdates = workspaceIds.map((workspaceId) => { const lockedWorkspace = workspaceById.get(workspaceId) @@ -752,3 +785,277 @@ export async function changeWorkspaceStoragePayerInTx( repairedWorkspaceLedger, } } + +/** + * Transfers each Project's retained current heads once and changes its owner in the same + * transaction. Callers acquire all affected owner locks before any payer or file locks; + * environment ownership changes required by Project lifecycle checks share this transaction. + * History is retained without an additional charge, and ownership transfers do not enforce quota. + */ +export async function changeProjectStoragePayersInTx( + tx: DbTransaction, + changes: readonly ChangeProjectStoragePayerParams[] +): Promise { + return changeProjectStoragePayers(tx, changes, false) +} + +async function changeProjectStoragePayers( + tx: DbTransaction, + changes: readonly ChangeProjectStoragePayerParams[], + deferPayerUpdates: boolean +): Promise { + if (!changes.length) return [] + const changesById = new Map(changes.map((change) => [change.projectId, change])) + if (changesById.size !== changes.length) + throw new Error('Storage payer batch contains duplicate Project IDs') + const projectIds = [...changesById.keys()].sort() + for (const id of projectIds) await lockProject(tx, id) + const owners = await tx + .select({ id: project.id, ownerId: project.ownerId, organizationId: project.organizationId }) + .from(project) + .where(inArray(project.id, projectIds)) + .orderBy(asc(project.id)) + .for('update') + const ownersById = new Map(owners.map((owner) => [owner.id, owner])) + const resultById = new Map() + for (const id of projectIds) { + const owner = ownersById.get(id) + const change = changesById.get(id) + if (!owner || !change) throw new Error(`Project ${id} not found during storage payer change`) + if ( + owner.ownerId !== change.expectedCurrentOwner.ownerId || + owner.organizationId !== change.expectedCurrentOwner.organizationId + ) + throw new Error(`Project ${id} owner changed before the transaction lock`) + const oldPayer: BillingEntity = owner.organizationId + ? { type: 'organization', id: owner.organizationId } + : { type: 'user', id: owner.ownerId } + const newPayer: BillingEntity = change.organizationId + ? { type: 'organization', id: change.organizationId } + : { type: 'user', id: change.ownerId } + resultById.set(id, { projectId: id, billableBytes: 0, oldPayer, newPayer }) + } + const totals = await getExactProjectStorageBytes(tx, projectIds) + for (const [projectId, bytes] of totals) { + const result = resultById.get(projectId) + if (!result) throw new Error('Unexpected Project storage aggregate') + result.billableBytes = bytes + } + if (!deferPayerUpdates) { + await applyPayerContributionTransfers(tx, [...resultById.values()]) + } + + for (const id of projectIds) { + const change = changesById.get(id) + if (!change) throw new Error(`Project ${id} change is missing`) + await tx + .update(project) + .set({ + ownerId: change.ownerId, + organizationId: change.organizationId, + updatedAt: new Date(), + }) + .where(eq(project.id, id)) + } + return changes.map((change) => { + const result = resultById.get(change.projectId) + if (!result) throw new Error(`Project ${change.projectId} result is missing`) + return result + }) +} + +async function getExactProjectStorageBytes(tx: DbTransaction, projectIds: string[]) { + const bytesById = new Map(projectIds.map((id) => [id, 0])) + if (!projectIds.length) return bytesById + const totals = await tx + .select({ + projectId: workspaceFiles.entityId, + bytes: sql`coalesce(sum(${workspaceFiles.sizeBytes}), 0)::text`, + invalidCount: sql`count(*) FILTER (WHERE ${workspaceFiles.sizeBytes} IS NULL OR ${workspaceFiles.sizeBytes} < 0)::integer`, + }) + .from(workspaceFiles) + .where( + and( + eq(workspaceFiles.entityType, 'project'), + eq(workspaceFiles.context, 'project'), + inArray(workspaceFiles.entityId, projectIds) + ) + ) + .groupBy(workspaceFiles.entityId) + for (const total of totals) { + if (!total.projectId || total.invalidCount > 0) + throw new Error('Project has invalid canonical size metadata') + if (!bytesById.has(total.projectId)) throw new Error('Unexpected Project storage aggregate') + bytesById.set(total.projectId, parseExactBytes(total.bytes, 'Project file')) + } + return bytesById +} + +/** Source repair clamps the entire outgoing batch before any incoming contribution is added. */ +async function applyPayerContributionTransfers( + tx: DbTransaction, + contributions: readonly { + billableBytes: number + oldPayer: BillingEntity + newPayer: BillingEntity | null + }[] +): Promise { + const payerByKey = new Map() + for (const { oldPayer, newPayer } of contributions) { + if (newPayer && getPayerKey(oldPayer) === getPayerKey(newPayer)) continue + payerByKey.set(getPayerKey(oldPayer), oldPayer) + if (newPayer) payerByKey.set(getPayerKey(newPayer), newPayer) + } + const usageByKey = await lockStoragePayers(tx, payerByKey) + const deltas = new Map() + for (const result of contributions) { + const sourceKey = getPayerKey(result.oldPayer) + const destinationKey = result.newPayer ? getPayerKey(result.newPayer) : null + if (sourceKey === destinationKey) continue + if (destinationKey && usageByKey.get(destinationKey) == null) + throw new Error(`Storage destination payer ${destinationKey} not found`) + const source = deltas.get(sourceKey) ?? { incomingBytes: 0, outgoingBytes: 0 } + source.outgoingBytes += result.billableBytes + deltas.set(sourceKey, source) + if (destinationKey) { + const destination = deltas.get(destinationKey) ?? { incomingBytes: 0, outgoingBytes: 0 } + destination.incomingBytes += result.billableBytes + deltas.set(destinationKey, destination) + } + } + const nextUsageByKey = new Map() + for (const [key, delta] of deltas) { + if (!Number.isSafeInteger(delta.incomingBytes) || !Number.isSafeInteger(delta.outgoingBytes)) { + throw new Error(`Storage payer ${key} delta exceeds the safe integer range`) + } + const current = usageByKey.get(key) + if (current == null) { + logger.warn('Storage source payer is missing during Project ownership transfer', { + payer: key, + outgoingBytes: delta.outgoingBytes, + }) + continue + } + if (current < delta.outgoingBytes) { + logger.warn('Clamping drifted resource source storage aggregate', { + payer: key, + currentBytes: current, + outgoingBytes: delta.outgoingBytes, + }) + } + const next = Math.max(0, current - delta.outgoingBytes) + delta.incomingBytes + if (!Number.isSafeInteger(next)) + throw new Error(`Storage payer ${key} total exceeds the safe integer range`) + nextUsageByKey.set(key, next) + } + await setStoragePayerUsagesBatch(tx, payerByKey, nextUsageByKey) +} + +/** Locks every owner, then every payer, before folding lifecycle transfers and removals once. */ +export async function changeProjectAndWorkspaceStoragePayersInTx( + tx: DbTransaction, + changes: { + projectChanges: readonly ChangeProjectStoragePayerParams[] + workspaceChanges: ChangeWorkspaceStoragePayerParams[] + /** The caller retires these Projects and their objects in this same transaction. */ + projectRemovals?: readonly ProjectStorageOwnerSnapshot[] + } +): Promise<{ + projects: ChangeProjectStoragePayerResult[] + workspaces: ChangeWorkspaceStoragePayerResult[] + removedProjects: RemovedProjectStorageContribution[] +}> { + const removals = changes.projectRemovals ?? [] + const projectIds = [...changes.projectChanges, ...removals] + .map((change) => change.projectId) + .sort() + if (new Set(projectIds).size !== projectIds.length) + throw new Error('Storage lifecycle batch contains duplicate Project IDs') + for (const id of projectIds) await lockProject(tx, id) + const projects = projectIds.length + ? await tx + .select({ + id: project.id, + ownerId: project.ownerId, + organizationId: project.organizationId, + }) + .from(project) + .where(inArray(project.id, projectIds)) + .orderBy(asc(project.id)) + .for('update') + : [] + const projectById = new Map(projects.map((owner) => [owner.id, owner])) + const workspaceIds = [ + ...new Set(changes.workspaceChanges.map((change) => change.workspaceId)), + ].sort() + const workspaces = workspaceIds.length + ? await tx + .select({ + id: workspace.id, + billedAccountUserId: workspace.billedAccountUserId, + organizationId: workspace.organizationId, + }) + .from(workspace) + .where(inArray(workspace.id, workspaceIds)) + .orderBy(asc(workspace.id)) + .for('no key update') + : [] + const workspaceById = new Map(workspaces.map((owner) => [owner.id, owner])) + const payerByKey = new Map() + const removedProjects: RemovedProjectStorageContribution[] = [] + for (const removal of removals) { + const owner = projectById.get(removal.projectId) + if ( + !owner || + owner.ownerId !== removal.ownerId || + owner.organizationId !== removal.organizationId + ) + throw new Error(`Project ${removal.projectId} owner changed before retirement`) + const payer: BillingEntity = owner.organizationId + ? { type: 'organization', id: owner.organizationId } + : { type: 'user', id: owner.ownerId } + payerByKey.set(getPayerKey(payer), payer) + removedProjects.push({ projectId: removal.projectId, billableBytes: 0, payer }) + } + for (const change of changes.projectChanges) { + const owner = projectById.get(change.projectId) + if (!owner) throw new Error(`Project ${change.projectId} not found during storage payer change`) + for (const value of [owner, change]) { + const payer: BillingEntity = value.organizationId + ? { type: 'organization', id: value.organizationId } + : { type: 'user', id: value.ownerId } + payerByKey.set(getPayerKey(payer), payer) + } + } + for (const change of changes.workspaceChanges) { + const owner = workspaceById.get(change.workspaceId) + if (!owner) + throw new Error(`Workspace ${change.workspaceId} not found during storage payer change`) + for (const value of [owner, change]) { + const payer = getWorkspacePayer(value) + payerByKey.set(getPayerKey(payer), payer) + } + } + await lockStoragePayers(tx, payerByKey) + const removedBytes = await getExactProjectStorageBytes( + tx, + removals.map((removal) => removal.projectId) + ) + for (const removal of removedProjects) { + const bytes = removedBytes.get(removal.projectId) + if (bytes === undefined) throw new Error('Project retirement storage total is missing') + removal.billableBytes = bytes + } + const projectResults = await changeProjectStoragePayers(tx, changes.projectChanges, true) + const workspaceResults = await changeWorkspaceStoragePayers(tx, changes.workspaceChanges, true) + await applyPayerContributionTransfers(tx, [ + ...projectResults, + ...workspaceResults, + ...removedProjects.map((removal) => ({ + billableBytes: removal.billableBytes, + oldPayer: removal.payer, + newPayer: null, + })), + ]) + return { projects: projectResults, workspaces: workspaceResults, removedProjects } +} diff --git a/apps/sim/lib/billing/storage/project-accounting.integration.ts b/apps/sim/lib/billing/storage/project-accounting.integration.ts new file mode 100644 index 00000000000..1024a42c816 --- /dev/null +++ b/apps/sim/lib/billing/storage/project-accounting.integration.ts @@ -0,0 +1,591 @@ +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname } from 'node:path' +import * as schema from '@sim/db/schema' +import { createPostgresStorageReconciliationStore } from '@sim/db/script-migrations/0003_backfill_workspace_storage_usage' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { createDeferred } from '@sim/testing/helpers/deferred' +import { getErrorMessage } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import { sql as query } from 'drizzle-orm' +import { drizzle } from 'drizzle-orm/postgres-js' +import postgres, { type Sql } from 'postgres' +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' +import { + type ProjectStorageBillingContext, + resolveProjectStorageBillingContext, + type StorageBillingContext, +} from '@/lib/billing/storage/context' +import { StorageLimitExceededError } from '@/lib/billing/storage/limits' +import { + changeProjectAndWorkspaceStoragePayersInTx, + changeProjectStoragePayersInTx, +} from '@/lib/billing/storage/payer-transfer' +import { + incrementStorageUsageForBillingContextInTx, + prepareProjectStorageMutationInTx, +} from '@/lib/billing/storage/tracking' +import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' +import { transferWorkspaceProjects } from '@/lib/projects/membership' + +const databaseUrl = readTestDatabaseUrl() +const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = + [] + +function check(name: string, run: () => Promise) { + it(name, async () => { + const started = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - started }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - started, + error: getErrorMessage(error), + }) + throw error + } + }) +} + +function context(projectId = 'project-a', limit = 1024): ProjectStorageBillingContext { + return { + projectId, + ownerId: 'user-a', + organizationId: 'organization-a', + billedAccountUserId: 'user-a', + billingEntity: { type: 'organization', id: 'organization-a' }, + plan: 'team', + customStorageLimitGB: limit / 1024 ** 3, + } +} + +describe('Project storage admission, transfer, and reconciliation in PostgreSQL', () => { + const schemaName = `project_storage_${generateId().replaceAll('-', '')}` + let sql: Sql + let admin: Sql + let database: ReturnType> + + async function waitForDatabaseLock(pid: number) { + for (let attempt = 0; attempt < 100; attempt += 1) { + const [state] = await sql<{ waiting: boolean }[]>`SELECT EXISTS ( + SELECT 1 FROM pg_stat_activity WHERE pid = ${pid} AND wait_event_type = 'Lock' + ) AS waiting` + if (state.waiting) return + await sleep(10) + } + throw new Error('Concurrent billing operation did not wait for its ownership or payer lock') + } + + beforeAll(async () => { + admin = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) + await admin.unsafe(`CREATE SCHEMA "${schemaName}"`) + sql = postgres(databaseUrl, { + max: 5, + onnotice: () => undefined, + connection: { search_path: schemaName }, + }) + database = drizzle(sql, { schema }) + await sql.unsafe(` + CREATE TABLE organization (id text PRIMARY KEY, storage_used_bytes bigint NOT NULL DEFAULT 0); + CREATE TABLE user_stats (id text PRIMARY KEY, user_id text UNIQUE NOT NULL, storage_used_bytes bigint NOT NULL DEFAULT 0); + CREATE TABLE member (id text PRIMARY KEY, organization_id text NOT NULL, user_id text NOT NULL, role text NOT NULL); + CREATE TABLE subscription (id text PRIMARY KEY, plan text NOT NULL, reference_id text NOT NULL, + stripe_customer_id text, stripe_subscription_id text, status text, period_start timestamp, + period_end timestamp, cancel_at_period_end boolean, cancel_at timestamp, canceled_at timestamp, + ended_at timestamp, seats integer, trial_start timestamp, trial_end timestamp, + billing_interval text, stripe_schedule_id text, metadata json, last_closed_period_start timestamp); + CREATE TABLE project (id text PRIMARY KEY, owner_id text NOT NULL, organization_id text, + name text NOT NULL DEFAULT 'Project', created_at timestamp NOT NULL DEFAULT now(), + archived_at timestamp, updated_at timestamp NOT NULL DEFAULT now()); + CREATE TABLE workspace (id text PRIMARY KEY, billed_account_user_id text NOT NULL, + owner_id text NOT NULL DEFAULT 'user-a', archived_at timestamp, updated_at timestamp DEFAULT now(), + organization_id text, storage_used_bytes bigint NOT NULL DEFAULT 0); + CREATE TABLE project_workspace (project_id text NOT NULL, workspace_id text UNIQUE NOT NULL, + created_at timestamp NOT NULL DEFAULT now()); + CREATE TABLE permissions (id text PRIMARY KEY, user_id text NOT NULL, entity_type text NOT NULL, + entity_id text NOT NULL, permission_type text NOT NULL); + CREATE TABLE permission_group (id text PRIMARY KEY, organization_id text, + config jsonb, updated_at timestamp); + CREATE TABLE workspace_files (id text PRIMARY KEY, entity_type text, entity_id text, + workspace_id text, context text NOT NULL, size_bytes bigint, deleted_at timestamp); + CREATE TABLE workspace_file_version (id text PRIMARY KEY, + file_id text REFERENCES workspace_files(id) ON DELETE CASCADE, size_bytes bigint NOT NULL); + CREATE TABLE knowledge_base (id text PRIMARY KEY, workspace_id text); + CREATE TABLE document (id text PRIMARY KEY, knowledge_base_id text, connector_id text, + file_size bigint, deleted_at timestamp); + CREATE TABLE knowledge_connector (id text PRIMARY KEY, knowledge_base_id text, + detached_at timestamp, detach_reserved_bytes bigint); + `) + }) + + beforeEach(async () => { + vi.stubEnv('FREE_STORAGE_LIMIT_GB', '1') + await sql`TRUNCATE workspace_file_version, workspace_files, project, workspace, organization, user_stats, member, subscription, project_workspace, permissions, permission_group` + await sql`INSERT INTO organization (id) VALUES ('organization-a'), ('organization-b')` + await sql`INSERT INTO user_stats (id, user_id) VALUES ('stats-a', 'user-a'), ('stats-b', 'user-b')` + await sql`INSERT INTO project (id, owner_id, organization_id) + VALUES ('project-a', 'user-a', 'organization-a'), ('project-b', 'user-a', 'organization-a')` + await sql`INSERT INTO workspace (id, billed_account_user_id, organization_id) + VALUES ('workspace-a', 'user-a', 'organization-a')` + }) + + afterAll(async () => { + try { + await sql?.end() + await admin?.unsafe(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`) + } finally { + await admin?.end() + const reportPath = process.env.PROJECT_STORAGE_REPORT_PATH + if (reportPath) { + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) + } + } + }) + + check('resolves the canonical payer and its plan within the caller transaction', async () => { + await database.transaction(async (tx) => { + await tx.execute(query`INSERT INTO member (id, organization_id, user_id, role) + VALUES ('member-a', 'organization-a', 'user-b', 'owner')`) + await tx.execute(query`INSERT INTO subscription (id, plan, reference_id, status, metadata) + VALUES ('org-sub', 'team_25000', 'organization-a', 'active', '{"customStorageLimitGB":2}'), + ('personal-sub', 'pro_4000', 'user-a', 'active', NULL), + ('unrelated-sub', 'enterprise', 'user-b', 'active', '{"customStorageLimitGB":100}')`) + const organizationContext = await resolveProjectStorageBillingContext( + { projectId: 'project-a', ownerId: 'user-a', organizationId: 'organization-a' }, + tx + ) + expect(organizationContext).toMatchObject({ + billedAccountUserId: 'user-b', + billingEntity: { type: 'organization', id: 'organization-a' }, + plan: 'team_25000', + customStorageLimitGB: 2, + }) + const prepared = await prepareProjectStorageMutationInTx(tx, organizationContext) + await prepared.applyDelta(40) + await tx.execute(query`UPDATE project SET organization_id = NULL WHERE id = 'project-b'`) + const personalContext = await resolveProjectStorageBillingContext( + { projectId: 'project-b', ownerId: 'user-a', organizationId: null }, + tx + ) + expect(personalContext).toMatchObject({ + billedAccountUserId: 'user-a', + billingEntity: { type: 'user', id: 'user-a' }, + plan: 'pro_4000', + customStorageLimitGB: null, + }) + const personalPrepared = await prepareProjectStorageMutationInTx(tx, personalContext) + await personalPrepared.applyDelta(20) + }) + const [organizationPayer] = await sql`SELECT storage_used_bytes::integer AS bytes + FROM organization WHERE id = 'organization-a'` + const [personalPayer] = await sql`SELECT storage_used_bytes::integer AS bytes + FROM user_stats WHERE user_id = 'user-a'` + expect(organizationPayer.bytes).toBe(40) + expect(personalPayer.bytes).toBe(20) + }) + + check( + 'combines Project and workspace transfers before repairing an underfunded shared payer', + async () => { + await sql`UPDATE project SET organization_id = NULL WHERE id = 'project-a'` + await sql`UPDATE workspace SET organization_id = NULL, billed_account_user_id = 'user-b', + storage_used_bytes = 100 WHERE id = 'workspace-a'` + await sql`UPDATE user_stats SET storage_used_bytes = 50 WHERE user_id = 'user-b'` + await sql`INSERT INTO workspace_files (id, entity_type, entity_id, workspace_id, context, size_bytes) + VALUES ('project-file', 'project', 'project-a', NULL, 'project', 40), + ('workspace-file', 'workspace', 'workspace-a', 'workspace-a', 'workspace', 100)` + await database.transaction((tx) => + changeProjectAndWorkspaceStoragePayersInTx(tx, { + projectChanges: [ + { + projectId: 'project-a', + ownerId: 'user-b', + organizationId: null, + expectedCurrentOwner: { ownerId: 'user-a', organizationId: null }, + }, + ], + workspaceChanges: [ + { + workspaceId: 'workspace-a', + billedAccountUserId: 'user-a', + organizationId: null, + expectedCurrentPayer: { billedAccountUserId: 'user-b', organizationId: null }, + }, + ], + }) + ) + expect( + await sql`SELECT user_id, storage_used_bytes::integer AS bytes FROM user_stats ORDER BY user_id` + ).toEqual([ + { user_id: 'user-a', bytes: 100 }, + { user_id: 'user-b', bytes: 40 }, + ]) + expect((await sql`SELECT owner_id FROM project WHERE id = 'project-a'`)[0].owner_id).toBe( + 'user-b' + ) + expect( + (await sql`SELECT billed_account_user_id FROM workspace WHERE id = 'workspace-a'`)[0] + .billed_account_user_id + ).toBe('user-a') + } + ) + + check( + 'Project environment transfer moves both ledgers once when personal owners differ', + async () => { + await sql`UPDATE project SET organization_id = NULL WHERE id = 'project-a'` + await sql`UPDATE workspace SET organization_id = NULL, billed_account_user_id = 'user-b', + storage_used_bytes = 20 WHERE id = 'workspace-a'` + await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('project-a', 'workspace-a')` + await sql`UPDATE user_stats SET storage_used_bytes = CASE user_id WHEN 'user-a' THEN 40 ELSE 20 END` + await sql`INSERT INTO workspace_files (id, entity_type, entity_id, workspace_id, context, size_bytes) + VALUES ('project-file', 'project', 'project-a', NULL, 'project', 40), + ('workspace-file', 'workspace', 'workspace-a', 'workspace-a', 'workspace', 20)` + await database.transaction((tx) => + transferWorkspaceProjects(tx, ['workspace-a'], 'organization-a', undefined, [ + { + workspaceId: 'workspace-a', + organizationId: 'organization-a', + billedAccountUserId: 'user-b', + expectedCurrentPayer: { organizationId: null, billedAccountUserId: 'user-b' }, + }, + ]) + ) + expect( + await sql`SELECT user_id, storage_used_bytes::integer AS bytes FROM user_stats ORDER BY user_id` + ).toEqual([ + { user_id: 'user-a', bytes: 0 }, + { user_id: 'user-b', bytes: 0 }, + ]) + expect( + ( + await sql`SELECT storage_used_bytes::integer AS bytes FROM organization WHERE id = 'organization-a'` + )[0].bytes + ).toBe(60) + expect( + (await sql`SELECT organization_id FROM workspace WHERE id = 'workspace-a'`)[0] + .organization_id + ).toBe('organization-a') + } + ) + + check( + 'account teardown waits for a Project write and transfers survivor ledgers as one batch', + async () => { + await sql`DELETE FROM project WHERE id = 'project-b'` + await sql`UPDATE project SET organization_id = NULL WHERE id = 'project-a'` + await sql`UPDATE workspace SET organization_id = NULL, storage_used_bytes = 20 WHERE id = 'workspace-a'` + await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('project-a', 'workspace-a')` + await sql`INSERT INTO permissions (id, user_id, entity_type, entity_id, permission_type) + VALUES ('admin', 'user-b', 'workspace', 'workspace-a', 'admin')` + await sql`UPDATE user_stats SET storage_used_bytes = 20 WHERE user_id = 'user-a'` + await sql`INSERT INTO workspace_files (id, entity_type, entity_id, workspace_id, context, size_bytes) + VALUES ('workspace-file', 'workspace', 'workspace-a', 'workspace-a', 'workspace', 20)` + const written = createDeferred() + const releaseWrite = createDeferred() + const teardownPid = createDeferred() + const write = database.transaction(async (tx) => { + const prepared = await prepareProjectStorageMutationInTx(tx, { + ...context(), + organizationId: null, + billingEntity: { type: 'user', id: 'user-a' }, + }) + await tx.execute(query`INSERT INTO workspace_files (id, entity_type, entity_id, context, size_bytes) + VALUES ('project-file', 'project', 'project-a', 'project', 40)`) + await prepared.applyDelta(40) + written.resolve() + await releaseWrite.promise + }) + await Promise.race([written.promise, write]) + const teardown = database.transaction(async (tx) => { + const [connection] = await tx.execute<{ pid: number }>( + query`SELECT pg_backend_pid() AS pid` + ) + teardownPid.resolve(connection.pid) + await prepareProjectsForAccountDeletion(tx, 'user-a', []) + }) + try { + await waitForDatabaseLock(await teardownPid.promise) + } finally { + releaseWrite.resolve() + } + await Promise.all([write, teardown]) + expect( + await sql`SELECT user_id, storage_used_bytes::integer AS bytes FROM user_stats ORDER BY user_id` + ).toEqual([ + { user_id: 'user-a', bytes: 0 }, + { user_id: 'user-b', bytes: 60 }, + ]) + expect((await sql`SELECT owner_id FROM project WHERE id = 'project-a'`)[0].owner_id).toBe( + 'user-b' + ) + expect( + (await sql`SELECT billed_account_user_id FROM workspace WHERE id = 'workspace-a'`)[0] + .billed_account_user_id + ).toBe('user-b') + } + ) + + check( + 'two Projects and a workspace share one locked quota without committing rejected file rows', + async () => { + const workspaceContext: StorageBillingContext = { + ...context('project-a', 60), + workspaceId: 'workspace-a', + } + const attempts = await Promise.allSettled([ + ...['project-a', 'project-b'].map((projectId) => + database.transaction(async (tx) => { + const prepared = await prepareProjectStorageMutationInTx(tx, context(projectId, 60)) + await tx.execute(query`INSERT INTO workspace_files (id, entity_type, entity_id, context, size_bytes) + VALUES (${projectId}, 'project', ${projectId}, 'project', 40)`) + await prepared.applyDelta(40) + }) + ), + database.transaction(async (tx) => { + await tx.execute(query`INSERT INTO workspace_files (id, workspace_id, context, size_bytes) + VALUES ('workspace-file', 'workspace-a', 'workspace', 40)`) + await incrementStorageUsageForBillingContextInTx(tx, workspaceContext, 40) + }), + ]) + expect(attempts.filter((result) => result.status === 'fulfilled')).toHaveLength(1) + for (const attempt of attempts) { + if (attempt.status === 'rejected') + expect(attempt.reason).toBeInstanceOf(StorageLimitExceededError) + } + const [payer] = + await sql`SELECT storage_used_bytes::integer AS bytes FROM organization WHERE id = 'organization-a'` + expect(payer.bytes).toBe(40) + expect(await sql`SELECT id FROM workspace_files`).toHaveLength(1) + } + ) + + check( + 'rejects stale owner snapshots and invalid deltas before a charge can survive', + async () => { + await sql`UPDATE project SET owner_id = 'user-b' WHERE id = 'project-a'` + await expect( + database.transaction((tx) => prepareProjectStorageMutationInTx(tx, context())) + ).rejects.toThrow(/changed/) + await sql`UPDATE project SET owner_id = 'user-a' WHERE id = 'project-a'` + for (const delta of [ + Number.NaN, + Number.POSITIVE_INFINITY, + 0.5, + Number.MAX_SAFE_INTEGER + 1, + ]) { + await expect( + database.transaction(async (tx) => { + const prepared = await prepareProjectStorageMutationInTx(tx, context()) + await prepared.applyDelta(delta) + }) + ).rejects.toThrow(/Invalid/) + } + await expect( + database.transaction(async (tx) => { + const prepared = await prepareProjectStorageMutationInTx(tx, context()) + await prepared.applyDelta(1) + await prepared.applyDelta(1) + }) + ).rejects.toThrow(/already/) + const [payer] = + await sql`SELECT storage_used_bytes::integer AS bytes FROM organization WHERE id = 'organization-a'` + expect(payer.bytes).toBe(0) + } + ) + + check( + 'keeps current-head deltas separate from retained history and rolls back failed finalization', + async () => { + await database.transaction(async (tx) => { + const prepared = await prepareProjectStorageMutationInTx(tx, context()) + await tx.execute( + query`INSERT INTO workspace_files VALUES ('file', 'project', 'project-a', NULL, 'project', 100, NULL)` + ) + await tx.execute(query`INSERT INTO workspace_file_version VALUES ('version', 'file', 500)`) + await prepared.applyDelta(100) + }) + await database.transaction(async (tx) => { + const prepared = await prepareProjectStorageMutationInTx(tx, context()) + await tx.execute( + query`UPDATE workspace_files SET size_bytes = 40, deleted_at = now() WHERE id = 'file'` + ) + await prepared.applyDelta(-60) + }) + await expect( + database.transaction(async (tx) => { + const prepared = await prepareProjectStorageMutationInTx(tx, context()) + await prepared.applyDelta(10) + throw new Error('finalization failed') + }) + ).rejects.toThrow('finalization failed') + const [payer] = + await sql`SELECT storage_used_bytes::integer AS bytes FROM organization WHERE id = 'organization-a'` + expect(payer.bytes).toBe(40) + await database.transaction(async (tx) => { + const prepared = await prepareProjectStorageMutationInTx(tx, context()) + await tx.execute(query`DELETE FROM workspace_files WHERE id = 'file'`) + await prepared.applyDelta(-40) + }) + expect(await sql`SELECT id FROM workspace_file_version`).toHaveLength(0) + const [empty] = + await sql`SELECT storage_used_bytes::integer AS bytes FROM organization WHERE id = 'organization-a'` + expect(empty.bytes).toBe(0) + } + ) + + check( + 'moves exact Project heads once across payers, including archived heads but excluding versions', + async () => { + await sql`INSERT INTO workspace_files VALUES + ('active', 'project', 'project-a', NULL, 'project', 40, NULL), + ('archived', 'project', 'project-a', NULL, 'project', 60, now()), + ('other', 'project', 'project-b', NULL, 'project', 25, NULL)` + await sql`INSERT INTO workspace_file_version VALUES ('version', 'active', 900)` + await sql`UPDATE organization SET storage_used_bytes = 125 WHERE id = 'organization-a'` + await database.transaction((tx) => + changeProjectStoragePayersInTx(tx, [ + { + projectId: 'project-a', + organizationId: null, + ownerId: 'user-b', + expectedCurrentOwner: { organizationId: 'organization-a', ownerId: 'user-a' }, + }, + ]) + ) + expect( + await sql`SELECT owner_id, organization_id FROM project WHERE id = 'project-a'` + ).toEqual([{ owner_id: 'user-b', organization_id: null }]) + expect( + await sql`SELECT storage_used_bytes::integer AS bytes FROM organization WHERE id = 'organization-a'` + ).toEqual([{ bytes: 25 }]) + expect( + await sql`SELECT storage_used_bytes::integer AS bytes FROM user_stats WHERE user_id = 'user-b'` + ).toEqual([{ bytes: 100 }]) + await expect( + database.transaction((tx) => + changeProjectStoragePayersInTx(tx, [ + { + projectId: 'project-a', + organizationId: 'organization-b', + ownerId: 'user-b', + expectedCurrentOwner: { organizationId: 'organization-a', ownerId: 'user-a' }, + }, + ]) + ) + ).rejects.toThrow(/changed/) + } + ) + + check( + 'reconciliation adds Project heads once and refuses incomplete canonical size metadata', + async () => { + await sql`INSERT INTO workspace_files VALUES + ('workspace-file', 'workspace', 'workspace-a', 'workspace-a', 'workspace', 20, NULL), + ('project-file', 'project', 'project-a', NULL, 'project', 40, NULL), + ('archived', 'project', 'project-a', NULL, 'project', 60, now()), + ('chat', NULL, NULL, 'workspace-a', 'mothership', 999, NULL)` + await sql`INSERT INTO workspace_file_version VALUES ('version', 'project-file', 900)` + const store = createPostgresStorageReconciliationStore(sql) + await store.reconcileWorkspaces(['workspace-a']) + await store.reconcileOrganization('organization-a') + expect( + await sql`SELECT storage_used_bytes::integer AS bytes FROM organization WHERE id = 'organization-a'` + ).toEqual([{ bytes: 120 }]) + await sql`UPDATE workspace_files SET size_bytes = NULL WHERE id = 'project-file'` + await expect(store.reconcileOrganization('organization-a')).rejects.toThrow( + /invalid canonical size/ + ) + expect( + await sql`SELECT storage_used_bytes::integer AS bytes FROM organization WHERE id = 'organization-a'` + ).toEqual([{ bytes: 120 }]) + } + ) + + check('reconciliation waits for a Project write and keeps the committed charge', async () => { + const changed = createDeferred() + const release = createDeferred() + const write = database.transaction(async (tx) => { + const prepared = await prepareProjectStorageMutationInTx(tx, context()) + await tx.execute( + query`INSERT INTO workspace_files VALUES ('file', 'project', 'project-a', NULL, 'project', 40, NULL)` + ) + await prepared.applyDelta(40) + changed.resolve() + await release.promise + }) + await Promise.race([changed.promise, write]) + const observer = postgres(databaseUrl, { + max: 1, + onnotice: () => undefined, + connection: { search_path: schemaName }, + }) + let reconciliation: Promise | undefined + try { + const [backend] = await observer<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + reconciliation = + createPostgresStorageReconciliationStore(observer).reconcileOrganization('organization-a') + await waitForDatabaseLock(backend.pid) + } finally { + release.resolve() + await write + await reconciliation + await observer.end() + } + expect( + await sql`SELECT storage_used_bytes::integer AS bytes FROM organization WHERE id = 'organization-a'` + ).toEqual([{ bytes: 40 }]) + }) + + check( + 'a queued ownership transfer includes the Project write that committed ahead of it', + async () => { + const changed = createDeferred() + const release = createDeferred() + const write = database.transaction(async (tx) => { + const prepared = await prepareProjectStorageMutationInTx(tx, context()) + await tx.execute( + query`INSERT INTO workspace_files VALUES ('file', 'project', 'project-a', NULL, 'project', 40, NULL)` + ) + await prepared.applyDelta(40) + changed.resolve() + await release.promise + }) + await Promise.race([changed.promise, write]) + const waiting = createDeferred() + let transfer: Promise | undefined + try { + transfer = database.transaction(async (tx) => { + const [backend] = await tx.execute<{ pid: number }>(query`SELECT pg_backend_pid() AS pid`) + waiting.resolve(backend.pid) + return changeProjectStoragePayersInTx(tx, [ + { + projectId: 'project-a', + organizationId: null, + ownerId: 'user-b', + expectedCurrentOwner: { organizationId: 'organization-a', ownerId: 'user-a' }, + }, + ]) + }) + await waitForDatabaseLock(await waiting.promise) + } finally { + release.resolve() + await write + await transfer + } + const store = createPostgresStorageReconciliationStore(sql) + await store.reconcileOrganization('organization-a') + await store.reconcileUser('user-b') + expect( + await sql`SELECT storage_used_bytes::integer AS bytes FROM organization WHERE id = 'organization-a'` + ).toEqual([{ bytes: 0 }]) + expect( + await sql`SELECT storage_used_bytes::integer AS bytes FROM user_stats WHERE user_id = 'user-b'` + ).toEqual([{ bytes: 40 }]) + } + ) +}) diff --git a/apps/sim/lib/billing/storage/tracking.ts b/apps/sim/lib/billing/storage/tracking.ts index e583883810f..5bd585a2f52 100644 --- a/apps/sim/lib/billing/storage/tracking.ts +++ b/apps/sim/lib/billing/storage/tracking.ts @@ -1,7 +1,7 @@ /** * Storage usage tracking for durable workspace and payer ledgers. * - * Every row lock here is `FOR NO KEY UPDATE`, never `FOR UPDATE`. The + * Storage counter row locks are `FOR NO KEY UPDATE`, never `FOR UPDATE`. The * `workspace`, `organization`, and `user_stats` rows these transactions lock * are foreign-key parents (49 tables reference `workspace` alone), so any * insert or update of a child row — a `workspace_files` row in this very @@ -17,7 +17,7 @@ * writes any of them or deletes a locked row. */ -import { organization, userStats, workspace } from '@sim/db/schema' +import { organization, project, userStats, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { generateId } from '@sim/utils/id' import { isRecordLike } from '@sim/utils/object' @@ -25,7 +25,11 @@ import { and, asc, eq, inArray, sql } from 'drizzle-orm' import { maybeNotifyLimit } from '@/lib/billing/core/limit-notifications' import type { HighestPrioritySubscription } from '@/lib/billing/core/plan' import type { BillingEntity } from '@/lib/billing/core/usage-log' -import type { StorageBillingContext } from '@/lib/billing/storage/context' +import type { + ProjectStorageBillingContext, + StorageBillingContext, + StoragePayerContext, +} from '@/lib/billing/storage/context' import { getLegacyStorageBillingEntity } from '@/lib/billing/storage/entity' import { getStorageLimitForBillingContext, @@ -36,7 +40,8 @@ import { StorageLimitExceededError, } from '@/lib/billing/storage/limits' import { getFreeTierLimit, isOrgScopedSubscription } from '@/lib/billing/subscriptions/utils' -import type { DbOrTx } from '@/lib/db/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' +import { lockProject } from '@/lib/projects/membership' const logger = createLogger('StorageTracking') @@ -110,7 +115,8 @@ async function mutateStorageUsage( executor: DbOrTx, billingEntity: Readonly, bytes: number, - mutation: StorageCounterMutation + mutation: StorageCounterMutation, + maximumUsage?: number ): Promise { if (billingEntity.type === 'organization') { const storageUsedBytes = @@ -120,7 +126,14 @@ async function mutateStorageUsage( const returned: unknown = await executor .update(organization) .set({ storageUsedBytes }) - .where(eq(organization.id, billingEntity.id)) + .where( + and( + eq(organization.id, billingEntity.id), + maximumUsage !== undefined && mutation === 'increment' + ? sql`${organization.storageUsedBytes} + ${bytes} <= ${maximumUsage}` + : undefined + ) + ) .returning({ storageUsedBytes: organization.storageUsedBytes }) return readReturnedStorageUsage(returned) } @@ -132,7 +145,14 @@ async function mutateStorageUsage( const returned: unknown = await executor .update(userStats) .set({ storageUsedBytes }) - .where(eq(userStats.userId, billingEntity.id)) + .where( + and( + eq(userStats.userId, billingEntity.id), + maximumUsage !== undefined && mutation === 'increment' + ? sql`${userStats.storageUsedBytes} + ${bytes} <= ${maximumUsage}` + : undefined + ) + ) .returning({ storageUsedBytes: userStats.storageUsedBytes }) return readReturnedStorageUsage(returned) } @@ -198,6 +218,84 @@ function assertWorkspaceStorageContext( return currentBillingEntity } +/** + * Establishes Project then payer locks before directory/file/version mutation. The returned + * one-shot delta belongs to this transaction; it is finalization admission, not a reservation. + * Project contribution is derived from retained file heads, so only the payer counter changes. + */ +export async function prepareProjectStorageMutationInTx( + tx: DbTransaction, + context: ProjectStorageBillingContext +): Promise<{ applyDelta(deltaBytes: number): Promise }> { + await lockProject(tx, context.projectId) + const [owner] = await tx + .select({ ownerId: project.ownerId, organizationId: project.organizationId }) + .from(project) + .where(eq(project.id, context.projectId)) + .for('update') + .limit(1) + if (!owner) throw new Error(`Project ${context.projectId} not found for storage accounting`) + const billingEntity: BillingEntity = owner.organizationId + ? { type: 'organization', id: owner.organizationId } + : { type: 'user', id: owner.ownerId } + if ( + owner.ownerId !== context.ownerId || + owner.organizationId !== context.organizationId || + billingEntity.type !== context.billingEntity.type || + billingEntity.id !== context.billingEntity.id || + (!owner.organizationId && context.billedAccountUserId !== owner.ownerId) + ) { + throw new Error( + `Storage payer changed for Project ${context.projectId}; resolve a fresh billing context` + ) + } + const currentUsage = await lockStorageUsageForMutation(tx, billingEntity) + if (!Number.isSafeInteger(currentUsage) || currentUsage < 0) { + throw new Error(`Invalid storage usage for payer ${getPayerKey(billingEntity)}`) + } + const limit = isStorageEnforcementEnabled() + ? getStorageLimitForBillingContext(context) + : undefined + let applied = false + return { + async applyDelta(deltaBytes) { + if (applied) throw new Error('Project storage delta was already applied') + if (!Number.isSafeInteger(deltaBytes)) throw new Error('Invalid Project storage delta') + applied = true + const latestUsage = await lockStorageUsageForMutation(tx, billingEntity) + const nextUsage = Math.max(0, latestUsage + deltaBytes) + if (!Number.isSafeInteger(nextUsage)) throw new Error('Invalid Project storage total') + if (deltaBytes > 0 && limit !== undefined && nextUsage > limit) { + throw new StorageLimitExceededError( + `Storage limit exceeded. Used: ${(nextUsage / 1024 ** 3).toFixed(2)}GB, Limit: ${(limit / 1024 ** 3).toFixed(0)}GB` + ) + } + if (deltaBytes === 0) return latestUsage + if (deltaBytes < 0 && latestUsage < -deltaBytes) { + logger.error('Clamping Project storage payer ledger underflow', { + projectId: context.projectId, + payer: getPayerKey(billingEntity), + currentBytes: latestUsage, + decrementBytes: -deltaBytes, + }) + } + const updated = await mutateStorageUsage( + tx, + billingEntity, + Math.abs(deltaBytes), + deltaBytes > 0 ? 'increment' : 'decrement', + Math.min(limit ?? Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER) + ) + if (updated === undefined) { + throw new StorageLimitExceededError( + 'Storage limit exceeded during Project file finalization' + ) + } + return updated + }, + } +} + /** * Applies signed workspace and legacy storage deltas in the canonical lock * order: sorted workspace rows, sorted user payer rows, then sorted @@ -522,7 +620,7 @@ async function mutateWorkspaceStorageUsage( * the counter mutation. */ export async function maybeNotifyStorageLimitForBillingContext( - context: StorageBillingContext, + context: StoragePayerContext & { readonly workspaceId?: string }, updatedUsage?: number, rearmOnly = false ): Promise { @@ -548,7 +646,7 @@ export async function maybeNotifyStorageLimitForBillingContext( rearmOnly, }) } catch (error) { - logger.error('Error evaluating workspace payer storage notification:', error) + logger.error('Error evaluating payer storage notification:', error) } } diff --git a/apps/sim/lib/core/orchestration/types.ts b/apps/sim/lib/core/orchestration/types.ts index b9ed678fd5a..0826bcb5cf6 100644 --- a/apps/sim/lib/core/orchestration/types.ts +++ b/apps/sim/lib/core/orchestration/types.ts @@ -1,3 +1,5 @@ +import { isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' + export type OrchestrationErrorCode = | 'validation' /** @@ -104,6 +106,8 @@ export function asOrchestrationError(error: unknown): OrchestrationError | null let current: unknown = error while (current instanceof Error) { if (current instanceof OrchestrationError) return current + if (isPayloadSizeLimitError(current)) + return new OrchestrationError('payload_too_large', current.message) current = current.cause } return null diff --git a/apps/sim/lib/core/outbox/processor.test.ts b/apps/sim/lib/core/outbox/processor.test.ts index a8c8d0b26b2..3f9c9344b1c 100644 --- a/apps/sim/lib/core/outbox/processor.test.ts +++ b/apps/sim/lib/core/outbox/processor.test.ts @@ -46,6 +46,9 @@ vi.mock('@/lib/uploads/contexts/workspace/workspace-file-live-doc-outbox', () => vi.mock('@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox', () => ({ workspaceFileStorageCleanupOutboxHandlers: {}, })) +vi.mock('@/lib/projects/files/prefix-cleanup', () => ({ + projectFilePrefixCleanupOutboxHandlers: {}, +})) vi.mock('@/lib/workflows/deployment-outbox', () => ({ workflowDeploymentOutboxHandlers: {} })) vi.mock('@/lib/workspaces/admin-move', () => ({ invitationMigrationOutboxHandlers: {} })) vi.mock('@/lib/workspaces/operations/outbox', () => ({ workspaceOperationOutboxHandlers: {} })) diff --git a/apps/sim/lib/core/outbox/processor.ts b/apps/sim/lib/core/outbox/processor.ts index 1d1e2e1da5e..4a2870a8d60 100644 --- a/apps/sim/lib/core/outbox/processor.ts +++ b/apps/sim/lib/core/outbox/processor.ts @@ -21,6 +21,8 @@ import { knowledgeDocumentProcessingOutboxHandlers } from '@/lib/knowledge/docum import { recoverKnowledgeDocumentProcessing } from '@/lib/knowledge/documents/processing-recovery' import { inboxCleanupOutboxHandlers } from '@/lib/mothership/inbox/cleanup-outbox' import { organizationResourceCleanupOutboxHandlers } from '@/lib/organizations/resource-cleanup' +import { projectFileDocumentOutboxHandlers } from '@/lib/projects/files/application/document-lifecycle' +import { projectFilePrefixCleanupOutboxHandlers } from '@/lib/projects/files/prefix-cleanup' import { workspaceFileLiveDocOutboxHandlers } from '@/lib/uploads/contexts/workspace/workspace-file-live-doc-outbox' import { workspaceFileStorageCleanupOutboxHandlers } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' import { workflowDeploymentOutboxHandlers } from '@/lib/workflows/deployment-outbox' @@ -48,6 +50,8 @@ const handlers = { ...permissionAccessRequestOutboxHandlers, ...workspaceFileLiveDocOutboxHandlers, ...workspaceFileStorageCleanupOutboxHandlers, + ...projectFilePrefixCleanupOutboxHandlers, + ...projectFileDocumentOutboxHandlers, ...workflowDeploymentOutboxHandlers, ...workspaceOperationOutboxHandlers, ...forkContentOutboxHandlers, diff --git a/apps/sim/lib/execution/payloads/file-secret-provenance.ts b/apps/sim/lib/execution/payloads/file-secret-provenance.ts index 1af18b999b3..1682d2d5361 100644 --- a/apps/sim/lib/execution/payloads/file-secret-provenance.ts +++ b/apps/sim/lib/execution/payloads/file-secret-provenance.ts @@ -39,6 +39,7 @@ export async function resolveStoredFileProvenanceSource( const metadata = await getFileMetadataByKey(file.key, undefined, { includeDeleted: true }) if (!metadata) return undefined if ( + !metadata.userId || (metadata.context !== 'workspace' && metadata.context !== 'mothership' && metadata.context !== 'execution') || diff --git a/apps/sim/lib/folders/naming.ts b/apps/sim/lib/folders/naming.ts index 3f8d66738e6..336843b41ad 100644 --- a/apps/sim/lib/folders/naming.ts +++ b/apps/sim/lib/folders/naming.ts @@ -1,6 +1,6 @@ import type { db } from '@sim/db' import { folder as folderTable } from '@sim/db/schema' -import { and, eq, isNull } from 'drizzle-orm' +import { and, eq, isNull, type SQL } from 'drizzle-orm' import type { FolderResourceType } from '@/lib/api/contracts/folders' type DbOrTx = Pick @@ -26,13 +26,28 @@ export async function deduplicateFolderName( requestedName: string, resourceType: FolderResourceType ): Promise { + return deduplicateFolderNameInScope( + tx, + and(eq(folderTable.workspaceId, workspaceId), eq(folderTable.resourceType, resourceType)), + parentId, + requestedName + ) +} + +/** Resolves a sibling name within a canonical owner/resource predicate supplied by its manager. */ +export async function deduplicateFolderNameInScope( + tx: DbOrTx, + scope: SQL | undefined, + parentId: string | null, + requestedName: string +): Promise { + if (!scope) throw new Error('Folder naming requires an owner scope') const siblingRows = await tx .select({ name: folderTable.name }) .from(folderTable) .where( and( - eq(folderTable.workspaceId, workspaceId), - eq(folderTable.resourceType, resourceType), + scope, parentId ? eq(folderTable.parentId, parentId) : isNull(folderTable.parentId), isNull(folderTable.deletedAt) ) diff --git a/apps/sim/lib/folders/queries.test.ts b/apps/sim/lib/folders/queries.test.ts index cc1d0cbb43b..8b8e1879e14 100644 --- a/apps/sim/lib/folders/queries.test.ts +++ b/apps/sim/lib/folders/queries.test.ts @@ -18,6 +18,8 @@ const ROW = { name: 'Reports', userId: 'u-1', workspaceId: 'ws-1', + entityType: null, + entityId: null, parentId: null, locked: false, sortOrder: 0, diff --git a/apps/sim/lib/folders/queries.ts b/apps/sim/lib/folders/queries.ts index 7c381e42c4b..225ccb1efa2 100644 --- a/apps/sim/lib/folders/queries.ts +++ b/apps/sim/lib/folders/queries.ts @@ -15,6 +15,11 @@ import { requireNonRootFolderPath, } from '@/lib/folders/paths' import { folderResourceLabel } from '@/lib/folders/resource-traits' +import { + isWorkspaceFolder, + requireWorkspaceFolder, + requireWorkspaceFolderCreator, +} from '@/lib/folders/scope' import type { FolderQueryScope } from '@/hooks/queries/utils/folder-keys' export type FolderSortBy = 'position' | 'name' | 'createdAt' | 'updatedAt' @@ -28,7 +33,7 @@ export type FolderSortBy = 'position' | 'name' | 'createdAt' | 'updatedAt' */ export function toFolderApi(row: typeof folder.$inferSelect): FolderApi { return { - ...row, + ...requireWorkspaceFolderCreator(row), createdAt: row.createdAt.toISOString(), updatedAt: row.updatedAt.toISOString(), deletedAt: row.deletedAt ? row.deletedAt.toISOString() : null, @@ -96,7 +101,7 @@ export async function findActiveFolder( ) .limit(1) - return row ?? null + return row && isWorkspaceFolder(row, workspaceId) ? row : null } /** @@ -182,7 +187,7 @@ export async function loadActiveFolderPathIndex( throw new FolderCollectionLimitExceededError('path index', options.maxRows) } - return buildFolderPathIndex(rows) + return buildFolderPathIndex(rows.map((row) => requireWorkspaceFolder(row, workspaceId))) } export interface FolderCollectionRoomOptions { @@ -312,7 +317,7 @@ export async function listActiveFolderRows( if (options.maxRows !== undefined && rows.length > options.maxRows) { throw new FolderCollectionLimitExceededError('list', options.maxRows) } - return rows + return rows.map((row) => requireWorkspaceFolder(row, workspaceId)) } /** @@ -343,7 +348,7 @@ export async function listFoldersForWorkspace( ) .orderBy(...listOrderBy(FOLDER_SORTS[sortBy], sortOrder)) - return rows.map(toFolderApi) + return rows.map((row) => toFolderApi(requireWorkspaceFolder(row, workspaceId))) } /** @@ -376,7 +381,7 @@ export async function findArchivedFolderIdByPath( throw new FolderCollectionLimitExceededError('path index', maxRows) } - const rowById = new Map(rows.map((row) => [row.id, row])) + const rowById = new Map(rows.map((row) => [row.id, requireWorkspaceFolder(row, workspaceId)])) const pathById = new Map() const resolvePath = (folderId: string, seen: Set): string | null => { diff --git a/apps/sim/lib/folders/scope.ts b/apps/sim/lib/folders/scope.ts index 049ce9212a2..76f4aa02e09 100644 --- a/apps/sim/lib/folders/scope.ts +++ b/apps/sim/lib/folders/scope.ts @@ -1,3 +1,6 @@ +import type { folder } from '@sim/db/schema' +import { OrchestrationError } from '@/lib/core/orchestration/types' + export interface FolderScopeOptions { /** * Whether the scope reaches nested folders. Absent means yes — a folder @@ -52,3 +55,40 @@ export interface FolderIdScope { /** Items carrying no folder id are in scope. */ includeRootItems: boolean } + +type FolderScope = Pick + +/** Narrows legacy and entity-owned rows to one canonical workspace folder. */ +export function isWorkspaceFolder( + row: T, + workspaceId?: string +): row is T & { workspaceId: string } { + return ( + typeof row.workspaceId === 'string' && + row.workspaceId.length > 0 && + (workspaceId === undefined || row.workspaceId === workspaceId) && + ((row.entityType === null && row.entityId === null) || + (row.entityType === 'workspace' && row.entityId === row.workspaceId)) + ) +} + +/** Rejects non-workspace ownership before projecting a workspace-only folder. */ +export function requireWorkspaceFolder( + row: T, + workspaceId?: string +): T & { workspaceId: string } { + if (!isWorkspaceFolder(row, workspaceId)) { + throw new OrchestrationError('not_found', 'Folder not found') + } + return row +} + +/** Workspace resources retain a live creator; detached Project attribution is not a workspace identity. */ +export function requireWorkspaceFolderCreator( + row: T, + workspaceId?: string +): T & { workspaceId: string; userId: string } { + const scoped = requireWorkspaceFolder(row, workspaceId) + if (!scoped.userId) throw new OrchestrationError('not_found', 'Folder not found') + return { ...scoped, userId: scoped.userId } +} diff --git a/apps/sim/lib/folders/subtree.ts b/apps/sim/lib/folders/subtree.ts index 90a610a043c..b0e4008e38f 100644 --- a/apps/sim/lib/folders/subtree.ts +++ b/apps/sim/lib/folders/subtree.ts @@ -1,3 +1,6 @@ +import type { FolderPathIndex } from '@/lib/folders/paths' +import type { FolderPathFilter } from '@/lib/folders/queries' + /** Minimal shape needed to walk a folder hierarchy — any row with an id and a parent. */ export interface FolderNode { id: string @@ -117,3 +120,19 @@ export function collectFolderDepths( return depths } + +/** Converts a resolved path filter into the SQL folder IDs for a shallow or recursive page. */ +export function resolveFolderScope( + folderIndex: FolderPathIndex, + folderFilter: FolderPathFilter, + recursive: boolean | undefined +): string | null | string[] | undefined { + if (folderFilter.kind !== 'folder') return undefined + if (!recursive) return folderFilter.folderId + if (folderFilter.folderId === null) return undefined + const childrenByParent = indexFolderChildren(folderIndex.rowById.values()) + return [ + folderFilter.folderId, + ...collectDescendantFolderIdsFrom(childrenByParent, folderFilter.folderId), + ] +} diff --git a/apps/sim/lib/knowledge/documents/service.ts b/apps/sim/lib/knowledge/documents/service.ts index 707c75b65c3..62db7a25d55 100644 --- a/apps/sim/lib/knowledge/documents/service.ts +++ b/apps/sim/lib/knowledge/documents/service.ts @@ -678,12 +678,14 @@ function durableSecretProvenanceFromWorkspaceFile( binding: FileMetadataRecord ): DurableSecretProvenance { if (provenance.status === 'unknown') return provenance + const sourceUserId = binding.userId + if (!sourceUserId || binding.entityType === 'project') return { status: 'unknown' } if (provenance.status === 'unrecorded') return EXACT_EMPTY_DURABLE_SECRET_PROVENANCE return { status: 'exact', entries: provenance.entries.map((entry) => ({ ...entry, - sourceUserId: binding.userId, + sourceUserId, ...(binding.workspaceId ? { sourceWorkspaceId: binding.workspaceId } : {}), })), } diff --git a/apps/sim/lib/mothership/tools/server/files/doc-compile-error.ts b/apps/sim/lib/mothership/tools/server/files/doc-compile-error.ts deleted file mode 100644 index 2c888d732c1..00000000000 --- a/apps/sim/lib/mothership/tools/server/files/doc-compile-error.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Signals that a generated document's artifact could not be served from the - * generation source. - * - * Two causes share the class. `pending` marks the transient one — the artifact - * is still compiling, so the caller should retry. Everything else is a permanent - * generation failure: the script raised, produced no output, or targeted a - * format with no fallback. Retrying those never succeeds, so a caller that - * reports both as "still being generated" tells the user to wait forever. - * - * Lives in its own leaf module — with no imports — so that callers which only - * need to *recognize* this error do not pull in `doc-compile`, which reaches - * `app/api/**` and therefore `next/server`. Application modules must not take - * that edge. - */ -export class DocCompileUserError extends Error { - /** True when the artifact is still compiling and a retry may succeed. */ - readonly pending: boolean - - constructor(message: string, options?: { pending?: boolean }) { - super(message) - this.name = 'DocCompileUserError' - this.pending = options?.pending ?? false - } -} diff --git a/apps/sim/lib/mothership/tools/server/files/edit-content.ts b/apps/sim/lib/mothership/tools/server/files/edit-content.ts index c9c0fd50f34..3eb0fbb7fd1 100644 --- a/apps/sim/lib/mothership/tools/server/files/edit-content.ts +++ b/apps/sim/lib/mothership/tools/server/files/edit-content.ts @@ -12,6 +12,7 @@ import { type BaseServerTool, type ServerToolContext, } from '@/lib/mothership/tools/server/base-tool' +import { getE2BDocFormat } from '@/lib/uploads/documents/compile' import { updateWorkspaceFileContent } from '@/lib/workspace-files/application/update-workspace-file-content' import { collectSimPageDiagnostics, @@ -20,7 +21,6 @@ import { isSimPageSource, SIM_PAGE_CONTENT_TYPE, } from '@/lib/workspace-files/page-compile' -import { getE2BDocFormat } from './doc-compile' import { buildEmbeddedImageRefWarning } from './embedded-image-refs' import { waitForLatestFileIntent } from './file-intent-store' import { compileDocForWrite, getDocumentFormatInfo, inferContentType } from './workspace-file' diff --git a/apps/sim/lib/mothership/tools/server/files/file-edit-roundtrip.test.ts b/apps/sim/lib/mothership/tools/server/files/file-edit-roundtrip.test.ts index f55cf189e81..44869517b43 100644 --- a/apps/sim/lib/mothership/tools/server/files/file-edit-roundtrip.test.ts +++ b/apps/sim/lib/mothership/tools/server/files/file-edit-roundtrip.test.ts @@ -18,7 +18,7 @@ vi.mock('@/lib/mothership/auth/file-delegation', () => ({ messageForCopilotFileError: (_error: unknown, fallback: string) => fallback, })) vi.mock('@/lib/execution/sandbox/run-task', () => ({ runSandboxTask })) -vi.mock('@/lib/mothership/tools/server/files/doc-compile', () => ({ +vi.mock('@/lib/uploads/documents/compile', () => ({ compileDoc, getE2BDocFormat: async () => null, DOCXJS_SOURCE_MIME: 'text/x-docxjs', 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..49af762dabc 100644 --- a/apps/sim/lib/mothership/tools/server/files/workspace-file.ts +++ b/apps/sim/lib/mothership/tools/server/files/workspace-file.ts @@ -19,8 +19,14 @@ import { type BaseServerTool, type ServerToolContext, } 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 { + compileDoc, + DOCXJS_SOURCE_MIME, + getE2BDocFormat, + PPTXGENJS_SOURCE_MIME, +} from '@/lib/uploads/documents/compile' +import { DocCompileUserError } from '@/lib/uploads/documents/compile-error' import { admitCreateWorkspaceFile, createWorkspaceFile, @@ -31,12 +37,6 @@ import { readWorkspaceFileContent } from '@/lib/workspace-files/application/read import { readWorkspaceFileMetadata } from '@/lib/workspace-files/application/read-workspace-file-metadata' import { renameWorkspaceFile } from '@/lib/workspace-files/application/rename-workspace-file' import type { SandboxTaskId } from '@/sandbox-tasks/registry' -import { - compileDoc, - DOCXJS_SOURCE_MIME, - getE2BDocFormat, - PPTXGENJS_SOURCE_MIME, -} from './doc-compile' import { buildEmbeddedImageRefWarning } from './embedded-image-refs' import { ensureCopilotFileFolderPath } from './file-folder-application' import { storeFileIntent } from './file-intent-store' diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts index 1831df0480d..de690987440 100644 --- a/apps/sim/lib/projects/account-deletion.ts +++ b/apps/sim/lib/projects/account-deletion.ts @@ -1,9 +1,16 @@ import { db } from '@sim/db' import { member, permissions, project, projectWorkspace, workspace } from '@sim/db/schema' import { and, asc, eq, inArray, ne, or, sql } from 'drizzle-orm' +import type { ProjectStorageOwnerSnapshot } from '@/lib/billing/storage/context' +import { + type ChangeProjectStoragePayerParams, + changeProjectAndWorkspaceStoragePayersInTx, +} from '@/lib/billing/storage/payer-transfer' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbOrTx, DbTransaction } from '@/lib/db/types' +import { purgeProjectFilesInTx } from '@/lib/projects/files/purge' import { lockProject, lockProjectBackfillWrites } from '@/lib/projects/membership' +import { planBilledAccountReassignmentsForUser } from '@/lib/workspaces/utils' async function loadRelatedProjects(executor: DbOrTx, userId: string, doomedWorkspaceIds: string[]) { return executor @@ -115,18 +122,16 @@ export async function prepareProjectsForAccountDeletion( tx: DbTransaction, userId: string, doomedWorkspaceIds: string[] -): Promise { +): Promise<{ storageCleanupEventIds: string[] }> { const ownedEnvironments = await tx .select({ id: workspace.id }) .from(workspace) - .where(eq(workspace.ownerId, userId)) - await lockProjectBackfillWrites(tx, [ - ...doomedWorkspaceIds, - ...ownedEnvironments.map((row) => row.id), - ]) + .where(or(eq(workspace.ownerId, userId), eq(workspace.billedAccountUserId, userId))) + const affectedWorkspaceIds = [...doomedWorkspaceIds, ...ownedEnvironments.map((row) => row.id)] + await lockProjectBackfillWrites(tx, affectedWorkspaceIds) const locked = new Set() for (;;) { - const records = await loadRelatedProjects(tx, userId, doomedWorkspaceIds) + const records = await loadRelatedProjects(tx, userId, affectedWorkspaceIds) const pending = records.filter((row) => !locked.has(row.id)) if (!pending.length) break for (const { id } of pending) { @@ -134,22 +139,56 @@ export async function prepareProjectsForAccountDeletion( locked.add(id) } } - const records = await loadRelatedProjects(tx, userId, doomedWorkspaceIds) + const records = await loadRelatedProjects(tx, userId, affectedWorkspaceIds) const doomed = new Set(doomedWorkspaceIds) + const projectChanges: ChangeProjectStoragePayerParams[] = [] + const projectRemovals: ProjectStorageOwnerSnapshot[] = [] for (const { id } of records) { - await lockProject(tx, id) const [record] = await tx.select().from(project).where(eq(project.id, id)) if (!record) continue const decision = await planProjectDeletion(tx, record, userId, doomed) if (decision.blocker) throw new OrchestrationError('conflict', decision.blocker) if (decision.remove) { - await tx.delete(projectWorkspace).where(eq(projectWorkspace.projectId, id)) - await tx.delete(project).where(eq(project.id, id)) + projectRemovals.push({ + projectId: id, + ownerId: record.ownerId, + organizationId: record.organizationId, + }) + continue } if (!decision.ownerId) continue + projectChanges.push({ + projectId: id, + ownerId: decision.ownerId, + organizationId: record.organizationId, + expectedCurrentOwner: { ownerId: record.ownerId, organizationId: record.organizationId }, + }) + } + const { changes: workspaceChanges } = await planBilledAccountReassignmentsForUser(userId, tx, { + excludeWorkspaceIds: doomedWorkspaceIds, + lockRows: true, + }) + const { removedProjects } = await changeProjectAndWorkspaceStoragePayersInTx(tx, { + projectChanges, + workspaceChanges, + projectRemovals, + }) + if (workspaceChanges.length) { await tx - .update(project) - .set({ ownerId: decision.ownerId, updatedAt: new Date() }) - .where(eq(project.id, id)) + .update(workspace) + .set({ updatedAt: new Date() }) + .where( + inArray( + workspace.id, + workspaceChanges.map((change) => change.workspaceId) + ) + ) + } + const storageCleanupEventIds: string[] = [] + for (const { projectId: id, billableBytes } of removedProjects) { + storageCleanupEventIds.push(...(await purgeProjectFilesInTx(tx, id, billableBytes))) + await tx.delete(projectWorkspace).where(eq(projectWorkspace.projectId, id)) + await tx.delete(project).where(eq(project.id, id)) } + return { storageCleanupEventIds } } diff --git a/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts b/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts new file mode 100644 index 00000000000..cbc1fa9f9b1 --- /dev/null +++ b/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts @@ -0,0 +1,586 @@ +import { mkdtempSync } from 'node:fs' +import { mkdir, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { dirname, join } from 'node:path' +import { db } from '@sim/db' +import { + folder, + outboxEvent, + permissions, + project, + projectWorkspace, + publicShare, + uploadSession, + user, + userStats, + workspace, + workspaceFileCollabState, + workspaceFileSecretProvenance, + workspaceFiles, + workspaceFileVersion, +} from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' +import { sha256Hex } from '@sim/security/hash' +import { createDeferred } from '@sim/testing/helpers/deferred' +import { setUploadDirServer, uploadsSetupMock } from '@sim/testing/mocks/uploads-setup.mock' +import { getErrorMessage } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import { and, eq, inArray, or, sql } from 'drizzle-orm' +import { NextRequest } from 'next/server' +import { afterAll, describe, expect, it, vi } from 'vitest' + +vi.mock('@/lib/uploads/core/setup.server', () => uploadsSetupMock) + +import { resolveProjectStorageBillingContext } from '@/lib/billing/storage/context' +import { prepareProjectStorageMutationInTx } from '@/lib/billing/storage/tracking' +import { processOutboxEventById } from '@/lib/core/outbox/service' +import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' +import { workspaceFileStorageCleanupOutboxHandlers } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' +import { UPLOAD_URL_TTL_MS } from '@/lib/uploads/upload-session/provider' +import { PROJECT_FILE_UPLOAD_BINDING_KEY } from '@/lib/uploads/upload-session/types' +import { deleteUserAccount } from '@/lib/users/account-deletion' +import { PUT as putUploadBytes } from '@/app/api/v2/uploads/[uploadId]/route' + +const storageRoot = mkdtempSync(join(tmpdir(), 'sim-project-purge-')) +setUploadDirServer(storageRoot) +const fixtures: { userId: string; projectId: string; workspaceId: string; fileIds: string[] }[] = [] +const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = + [] + +function check(name: string, run: () => Promise) { + it(name, async () => { + const started = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - started }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - started, + error: getErrorMessage(error), + }) + throw error + } + }) +} + +async function seedUpload(f: { userId: string; projectId: string }, fileName: string) { + const id = generateId() + const uploadToken = generateId() + const finalKey = `project/${f.projectId}/${id}/${fileName}` + const contentType = 'application/octet-stream' + await db.insert(uploadSession).values({ + id, + tokenHash: sha256Hex(uploadToken), + userId: f.userId, + purpose: 'project_file', + method: 'put', + storageContext: 'project', + finalKey, + storageProvider: 'local', + fileName, + contentType, + fileSize: 4, + metadata: { + [PROJECT_FILE_UPLOAD_BINDING_KEY]: { entityType: 'project', entityId: f.projectId }, + }, + expiresAt: new Date(Date.now() + UPLOAD_URL_TTL_MS), + }) + return { id, uploadToken, finalKey, contentType } +} + +async function fixture() { + const userId = generateId() + const workspaceId = generateId() + await db.insert(user).values({ + id: userId, + email: `${userId}@purge.invalid`, + name: 'Purge fixture', + emailVerified: true, + createdAt: new Date(), + updatedAt: new Date(), + }) + await db.insert(userStats).values({ id: generateId(), userId, storageUsedBytes: 18 }) + await insertWorkspaceFixture(db, { + id: workspaceId, + ownerId: userId, + billedAccountUserId: userId, + name: 'Private environment', + }) + await db.insert(permissions).values({ + id: generateId(), + userId, + entityType: 'workspace', + entityId: workspaceId, + permissionType: 'admin', + }) + const [binding] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, workspaceId)) + if (!binding) throw new Error('Private Project fixture missing') + const projectId = binding.projectId + const folderId = generateId() + const fileIds = [generateId(), generateId()] + const keys = [ + `project/${projectId}/current.md`, + `project/${projectId}/archived.md`, + `project/${projectId}/old.md`, + ] + await db.insert(folder).values({ + id: folderId, + userId, + resourceType: 'file', + name: 'Docs', + entityType: 'project', + entityId: projectId, + }) + const revision = new Date() + await db.insert(workspaceFiles).values( + fileIds.map((id, index) => ({ + id, + userId, + entityType: 'project', + entityId: projectId, + context: 'project', + key: keys[index], + folderId, + originalName: index ? 'archived.md' : 'current.md', + contentType: 'text/markdown', + sizeBytes: index ? 5 : 3, + deletedAt: index ? new Date() : null, + contentUpdatedAt: revision, + secretProvenanceVersion: 1, + })) + ) + await db.insert(workspaceFileVersion).values([ + { + id: generateId(), + fileId: fileIds[0], + version: 1, + key: keys[2], + sizeBytes: 100, + contentType: 'text/markdown', + source: 'api', + authorUserIds: [userId], + supersededAt: revision, + }, + { + id: generateId(), + fileId: fileIds[0], + version: 2, + key: keys[0], + sizeBytes: 3, + contentType: 'text/markdown', + source: 'api', + authorUserIds: [userId], + }, + ]) + await db.insert(workspaceFileSecretProvenance).values({ + fileId: fileIds[0], + contentUpdatedAt: revision, + status: 'exact', + entries: [], + }) + await db + .insert(workspaceFileCollabState) + .values({ fileId: fileIds[0], docState: Buffer.from([0]), sourceHash: 'fixture' }) + await db.insert(publicShare).values({ + id: generateId(), + resourceType: 'file', + resourceId: fileIds[0], + entityType: 'project', + entityId: projectId, + createdBy: userId, + token: generateId(), + }) + for (const [index, key] of keys.entries()) { + await mkdir(dirname(join(storageRoot, key)), { recursive: true }) + await writeFile( + join(storageRoot, key), + index === 0 ? 'new' : index === 1 ? 'older' : 'x'.repeat(100) + ) + } + const f = { userId, projectId, workspaceId, fileIds, folderId, keys } + fixtures.push(f) + return f +} + +async function cleanupEvents(projectId: string) { + return db + .select() + .from(outboxEvent) + .where( + and( + eq(outboxEvent.eventType, 'workspace-file.storage.cleanup'), + sql`${outboxEvent.payload}->>'key' LIKE ${`project/${projectId}/%`}` + ) + ) +} + +async function payerUsage(userId: string) { + const [row] = await db + .select({ bytes: userStats.storageUsedBytes }) + .from(userStats) + .where(eq(userStats.userId, userId)) + return row?.bytes +} + +async function erasePrivateProject(f: Awaited>) { + return db.transaction(async (tx) => { + const result = await prepareProjectsForAccountDeletion(tx, f.userId, [f.workspaceId]) + await tx.delete(workspace).where(eq(workspace.id, f.workspaceId)) + return result + }) +} + +describe('Private Project teardown and durable object cleanup', () => { + check( + 'retires retained heads once, preserves unrelated usage, and queues current/history objects atomically', + async () => { + const f = await fixture() + await expect(db.delete(project).where(eq(project.id, f.projectId))).rejects.toThrow() + await erasePrivateProject(f) + expect(await payerUsage(f.userId)).toBe(10) + expect( + await db.select().from(workspaceFiles).where(inArray(workspaceFiles.id, f.fileIds)) + ).toEqual([]) + expect( + await db + .select() + .from(workspaceFileVersion) + .where(inArray(workspaceFileVersion.fileId, f.fileIds)) + ).toEqual([]) + expect( + await db + .select() + .from(workspaceFileCollabState) + .where(inArray(workspaceFileCollabState.fileId, f.fileIds)) + ).toEqual([]) + expect( + await db + .select() + .from(workspaceFileSecretProvenance) + .where(inArray(workspaceFileSecretProvenance.fileId, f.fileIds)) + ).toEqual([]) + expect(await db.select().from(folder).where(eq(folder.id, f.folderId))).toEqual([]) + expect( + await db.select().from(publicShare).where(inArray(publicShare.resourceId, f.fileIds)) + ).toEqual([]) + expect(await db.select().from(project).where(eq(project.id, f.projectId))).toEqual([]) + const events = await cleanupEvents(f.projectId) + expect(events.map((event) => event.payload)).toEqual( + expect.arrayContaining(f.keys.map((key) => ({ key, context: 'project' }))) + ) + expect(events).toHaveLength(3) + for (const key of f.keys) + await expect(readFile(join(storageRoot, key))).resolves.toBeInstanceOf(Buffer) + } + ) + + check( + 'rolls back all purged metadata, accounting, and cleanup work when account teardown fails, then retries once', + async () => { + const f = await fixture() + await expect( + db.transaction(async (tx) => { + await prepareProjectsForAccountDeletion(tx, f.userId, [f.workspaceId]) + await tx.delete(workspace).where(eq(workspace.id, f.workspaceId)) + throw new Error('Later account teardown failed') + }) + ).rejects.toThrow('Later account teardown failed') + expect(await payerUsage(f.userId)).toBe(18) + expect( + await db.select().from(workspaceFiles).where(inArray(workspaceFiles.id, f.fileIds)) + ).toHaveLength(2) + expect(await cleanupEvents(f.projectId)).toEqual([]) + expect( + await db + .select() + .from(outboxEvent) + .where(sql`${outboxEvent.payload}->>'projectId' = ${f.projectId}`) + ).toEqual([]) + await erasePrivateProject(f) + await erasePrivateProject(f) + expect(await payerUsage(f.userId)).toBe(10) + expect(await cleanupEvents(f.projectId)).toHaveLength(3) + } + ) + + check('queues behind a Project mutation and retires its committed size', async () => { + const f = await fixture() + const held = createDeferred() + const release = createDeferred() + const mutation = db.transaction(async (tx) => { + const context = await resolveProjectStorageBillingContext( + { projectId: f.projectId, ownerId: f.userId, organizationId: null }, + tx + ) + const accounting = await prepareProjectStorageMutationInTx(tx, context) + await tx + .update(workspaceFiles) + .set({ sizeBytes: 10 }) + .where(eq(workspaceFiles.id, f.fileIds[0])) + await accounting.applyDelta(7) + held.resolve() + await release.promise + }) + await Promise.race([mutation, held.promise]) + const pid = createDeferred() + const deletion = db.transaction(async (tx) => { + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + pid.resolve(connection.pid) + await prepareProjectsForAccountDeletion(tx, f.userId, [f.workspaceId]) + await tx.delete(workspace).where(eq(workspace.id, f.workspaceId)) + }) + const observedDeletion = deletion.then( + () => null, + (error: unknown) => error + ) + try { + let waiting = false + for (let attempt = 0; attempt < 100; attempt++) { + const [state] = await db.execute<{ waiting: boolean }>( + sql`SELECT EXISTS (SELECT 1 FROM pg_stat_activity WHERE pid = ${await pid.promise} AND wait_event_type = 'Lock') AS waiting` + ) + if (state.waiting) { + waiting = true + break + } + await sleep(10) + } + expect(waiting).toBe(true) + } finally { + release.resolve() + } + await mutation + expect(await observedDeletion).toBeNull() + expect(await payerUsage(f.userId)).toBe(10) + expect(await cleanupEvents(f.projectId)).toHaveLength(3) + }) + + check( + 'account deletion commits despite an object-store failure and its durable cleanup retries successfully', + async () => { + const f = await fixture() + const obstructed = join(storageRoot, f.keys[0]) + await rm(obstructed) + await mkdir(obstructed) + await deleteUserAccount(f.userId) + expect(await db.select().from(user).where(eq(user.id, f.userId))).toEqual([]) + expect(await db.select().from(project).where(eq(project.id, f.projectId))).toEqual([]) + const events = await cleanupEvents(f.projectId) + const failed = events.find((event) => (event.payload as { key?: string }).key === f.keys[0]) + expect(failed).toMatchObject({ status: 'pending', attempts: 1 }) + if (!failed) throw new Error('Retryable cleanup missing') + await rm(obstructed, { recursive: true }) + await writeFile(obstructed, 'new') + await db + .update(outboxEvent) + .set({ availableAt: new Date(0) }) + .where(eq(outboxEvent.id, failed.id)) + expect( + await processOutboxEventById(failed.id, workspaceFileStorageCleanupOutboxHandlers) + ).toBe('completed') + await expect(readFile(obstructed)).rejects.toMatchObject({ code: 'ENOENT' }) + expect( + await processOutboxEventById(failed.id, workspaceFileStorageCleanupOutboxHandlers) + ).toBe('completed') + } + ) + + check( + 'retirement revokes byte transfers and durably sweeps derived and late objects without touching a live Project', + async () => { + const f = await fixture() + const live = await fixture() + const upload = await seedUpload(f, 'pending.bin') + const derived = `project/${f.projectId}/compiled/derived.pdf` + await mkdir(dirname(join(storageRoot, derived)), { recursive: true }) + await writeFile(join(storageRoot, derived), 'derived') + const retiredAt = Date.now() + await erasePrivateProject(f) + const [retiredUpload] = await db + .select() + .from(uploadSession) + .where(eq(uploadSession.id, upload.id)) + expect(retiredUpload.status).toBe('aborting') + expect(retiredUpload.expiresAt.getTime()).toBeLessThanOrEqual(Date.now()) + const events = await db + .select() + .from(outboxEvent) + .where( + and( + eq(outboxEvent.eventType, 'project-file.storage.prefix-cleanup'), + sql`${outboxEvent.payload}->>'projectId' = ${f.projectId}` + ) + ) + expect(events).toHaveLength(2) + const immediate = events.find( + (event) => event.availableAt.getTime() < retiredAt + UPLOAD_URL_TTL_MS + ) + const final = events.find( + (event) => event.availableAt.getTime() >= retiredAt + UPLOAD_URL_TTL_MS + ) + if (!immediate || !final) throw new Error('Immediate and capability-horizon sweeps required') + const { projectFilePrefixCleanupOutboxHandlers } = await import( + '@/lib/projects/files/prefix-cleanup' + ) + expect( + await processOutboxEventById(immediate.id, projectFilePrefixCleanupOutboxHandlers) + ).toBe('completed') + await expect(readFile(join(storageRoot, derived))).rejects.toMatchObject({ code: 'ENOENT' }) + expect(await readFile(join(storageRoot, live.keys[0]), 'utf8')).toBe('new') + await mkdir(dirname(join(storageRoot, upload.finalKey)), { recursive: true }) + await writeFile(join(storageRoot, upload.finalKey), 'late') + expect(await processOutboxEventById(final.id, projectFilePrefixCleanupOutboxHandlers)).toBe( + 'pending' + ) + expect(await readFile(join(storageRoot, upload.finalKey), 'utf8')).toBe('late') + await db + .update(outboxEvent) + .set({ availableAt: new Date(0) }) + .where(eq(outboxEvent.id, final.id)) + expect(await processOutboxEventById(final.id, projectFilePrefixCleanupOutboxHandlers)).toBe( + 'completed' + ) + await expect(readFile(join(storageRoot, upload.finalKey))).rejects.toMatchObject({ + code: 'ENOENT', + }) + const unsafe = generateId() + await db.insert(outboxEvent).values({ + id: unsafe, + eventType: immediate.eventType, + payload: { projectId: live.projectId }, + }) + expect(await processOutboxEventById(unsafe, projectFilePrefixCleanupOutboxHandlers)).toBe( + 'pending' + ) + expect(await readFile(join(storageRoot, live.keys[0]), 'utf8')).toBe('new') + } + ) + + check( + 'a local stream finishing after retirement queues cleanup instead of leaving a late object', + async () => { + const f = await fixture() + const created = await seedUpload(f, 'stream.bin') + const entered = createDeferred() + const release = createDeferred() + const body = new ReadableStream( + { + async pull(controller) { + entered.resolve() + await release.promise + controller.enqueue(Buffer.from('late')) + controller.close() + }, + }, + { highWaterMark: 0 } + ) + const request = new NextRequest(`http://localhost:3000/api/v2/uploads/${created.id}`, { + method: 'PUT', + headers: { 'upload-token': created.uploadToken, 'content-type': created.contentType }, + body, + duplex: 'half', + }) + const pending = putUploadBytes(request, { params: Promise.resolve({ uploadId: created.id }) }) + await entered.promise + try { + await erasePrivateProject(f) + } finally { + release.resolve() + } + const response = await pending + expect(response.status).toBe(409) + const events = await db + .select() + .from(outboxEvent) + .where( + and( + eq(outboxEvent.eventType, 'project-file.storage.prefix-cleanup'), + sql`${outboxEvent.payload}->>'projectId' = ${f.projectId}` + ) + ) + expect(events.length).toBeGreaterThanOrEqual(4) + const { projectFilePrefixCleanupOutboxHandlers } = await import( + '@/lib/projects/files/prefix-cleanup' + ) + for (const event of events.filter((row) => row.availableAt.getTime() <= Date.now())) + await processOutboxEventById(event.id, projectFilePrefixCleanupOutboxHandlers) + await expect(readFile(join(storageRoot, created.finalKey))).rejects.toMatchObject({ + code: 'ENOENT', + }) + } + ) + + check('prefix sweeps continue bounded batches without consuming failure retries', async () => { + const f = await fixture() + const prefix = join(storageRoot, `project/${f.projectId}`) + await Promise.all( + Array.from({ length: 510 }, async (_, index) => { + await writeFile(join(prefix, `cache-${index}.bin`), 'x') + }) + ) + await erasePrivateProject(f) + const events = await db + .select() + .from(outboxEvent) + .where( + and( + eq(outboxEvent.eventType, 'project-file.storage.prefix-cleanup'), + sql`${outboxEvent.payload}->>'projectId' = ${f.projectId}` + ) + ) + expect(events).toHaveLength(2) + const event = events.find((row) => row.availableAt.getTime() <= Date.now()) + if (!event) throw new Error('Immediate sweep missing') + const { projectFilePrefixCleanupOutboxHandlers } = await import( + '@/lib/projects/files/prefix-cleanup' + ) + expect(await processOutboxEventById(event.id, projectFilePrefixCleanupOutboxHandlers)).toBe( + 'pending' + ) + const [continued] = await db.select().from(outboxEvent).where(eq(outboxEvent.id, event.id)) + expect(continued.attempts).toBe(0) + await db + .update(outboxEvent) + .set({ availableAt: new Date(0) }) + .where(eq(outboxEvent.id, event.id)) + expect(await processOutboxEventById(event.id, projectFilePrefixCleanupOutboxHandlers)).toBe( + 'completed' + ) + await expect(readFile(join(prefix, 'cache-509.bin'))).rejects.toMatchObject({ code: 'ENOENT' }) + }) +}) + +afterAll(async () => { + try { + for (const f of fixtures) { + await db.delete(workspaceFiles).where(inArray(workspaceFiles.id, f.fileIds)) + await db + .delete(folder) + .where(and(eq(folder.entityType, 'project'), eq(folder.entityId, f.projectId))) + await deleteWorkspaceFixture(db, eq(workspace.id, f.workspaceId)) + await db.delete(uploadSession).where(eq(uploadSession.userId, f.userId)) + await db.delete(user).where(eq(user.id, f.userId)) + await db + .delete(outboxEvent) + .where( + or( + sql`${outboxEvent.payload}->>'key' LIKE ${`project/${f.projectId}/%`}`, + sql`${outboxEvent.payload}->>'projectId' = ${f.projectId}` + ) + ) + } + await rm(storageRoot, { recursive: true, force: true }) + } finally { + const reportPath = process.env.PROJECT_PURGE_REPORT_PATH + if (reportPath) { + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) + } + } +}) diff --git a/apps/sim/lib/projects/files/__integration__/retention.integration.ts b/apps/sim/lib/projects/files/__integration__/retention.integration.ts new file mode 100644 index 00000000000..3e73ea96322 --- /dev/null +++ b/apps/sim/lib/projects/files/__integration__/retention.integration.ts @@ -0,0 +1,514 @@ +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname } from 'node:path' +import { db } from '@sim/db' +import { + folder, + member, + organization, + outboxEvent, + permissions, + projectWorkspace, + subscription, + user, + userStats, + workspace, + workspaceFiles, + workspaceFileVersion, +} from '@sim/db/schema' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' +import { createDeferred } from '@sim/testing/helpers/deferred' +import { envFlagsMock, resetEnvFlagsMock, setEnvFlags } from '@sim/testing/mocks/env-flags.mock' +import { getErrorMessage } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import { and, eq, inArray, sql } from 'drizzle-orm' +import postgres from 'postgres' +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' + +vi.mock('@/lib/core/config/env-flags', () => envFlagsMock) + +import { createSessionPrincipal } from '@sim/testing/factories/principal.factory' +import { type CleanupJobPayload, runCleanupWithLimits } from '@/lib/billing/cleanup-dispatcher' +import { createCleanupBudgets } from '@/lib/cleanup/limits' +import { authorizeProject } from '@/lib/projects/application/authorization' +import { projectOperations } from '@/lib/projects/application/operations' +import { runCleanupFileVersions } from '@/background/cleanup-file-versions' +import { runCleanupSoftDeletes } from '@/background/cleanup-soft-deletes' + +const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = + [] +const fixtures: { + ownerId: string + creatorId: string + organizationId: string | null + workspaceId: string + projectId: string +}[] = [] +const HOUR = 60 * 60 * 1000 +const control = postgres(readTestDatabaseUrl(), { max: 2, onnotice: () => undefined }) + +function check(name: string, run: () => Promise) { + it(name, async () => { + const started = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - started }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - started, + error: getErrorMessage(error), + }) + throw error + } + }) +} + +beforeEach(() => { + setEnvFlags({ isBillingEnabled: true, isDataRetentionEnabled: true }) +}) + +async function fixture(plan: 'free' | 'pro' | 'enterprise' = 'free') { + const ownerId = generateId() + const creatorId = generateId() + const organizationId = plan === 'enterprise' ? generateId() : null + const workspaceId = generateId() + await db.insert(user).values( + [ownerId, creatorId].map((id) => ({ + id, + email: `${id}@retention.invalid`, + name: 'Retention fixture', + emailVerified: true, + createdAt: new Date(), + updatedAt: new Date(), + })) + ) + await db.insert(userStats).values([ + { id: generateId(), userId: ownerId, storageUsedBytes: 15 }, + { id: generateId(), userId: creatorId, storageUsedBytes: 0 }, + ]) + await db + .insert(subscription) + .values({ id: generateId(), referenceId: creatorId, plan: 'pro', status: 'active' }) + if (organizationId) { + await db.insert(organization).values({ + id: organizationId, + name: 'Retention organization', + slug: organizationId, + storageUsedBytes: 15, + dataRetentionSettings: { + fileVersionRetentionHours: 100, + softDeleteRetentionHours: 100, + retentionOverrides: [ + { workspaceId, fileVersionRetentionHours: 1, softDeleteRetentionHours: 1 }, + ], + }, + }) + await db.insert(member).values({ + id: generateId(), + userId: ownerId, + organizationId, + role: 'owner', + createdAt: new Date(), + }) + } + if (plan !== 'free') + await db.insert(subscription).values({ + id: generateId(), + referenceId: organizationId ?? ownerId, + plan, + status: 'active', + metadata: plan === 'enterprise' ? {} : null, + }) + await insertWorkspaceFixture(db, { + id: workspaceId, + ownerId, + billedAccountUserId: ownerId, + name: 'Retention environment', + organizationId, + workspaceMode: organizationId ? 'organization' : 'personal', + }) + const [binding] = await db + .select({ projectId: projectWorkspace.projectId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, workspaceId)) + if (!binding) throw new Error('Retention fixture Project missing') + await db.insert(permissions).values({ + id: generateId(), + userId: ownerId, + entityType: 'workspace', + entityId: workspaceId, + permissionType: 'admin', + }) + const result = { ownerId, creatorId, organizationId, workspaceId, projectId: binding.projectId } + fixtures.push(result) + return result +} + +async function seedFile( + f: Awaited>, + ageHours: number, + archived = false, + kind: 'project' | 'workspace' = 'project' +) { + const fileId = generateId() + const cutoff = new Date(Date.now() - ageHours * HOUR) + const ownerId = kind === 'project' ? f.projectId : f.workspaceId + const keys = Array.from( + { length: 12 }, + (_, index) => `${kind}/${ownerId}/${fileId}-${index + 1}.md` + ) + await db.insert(workspaceFiles).values({ + id: fileId, + userId: f.creatorId, + entityType: kind, + entityId: ownerId, + context: kind, + workspaceId: kind === 'workspace' ? f.workspaceId : null, + key: keys[11], + originalName: `${fileId}.md`, + contentType: 'text/markdown', + sizeBytes: 5, + deletedAt: archived ? cutoff : null, + }) + await db.insert(workspaceFileVersion).values( + keys.map((key, index) => ({ + id: generateId(), + fileId, + workspaceId: kind === 'workspace' ? f.workspaceId : null, + version: index + 1, + key, + sizeBytes: index === 11 ? 5 : 100, + contentType: 'text/markdown', + source: 'api' as const, + supersededAt: index === 11 ? null : cutoff, + })) + ) + return { fileId, keys } +} + +function payload(projectId: string) { + return { + workspaceIds: [], + projectIds: [projectId], + plan: 'free' as const, + retentionHours: 1, + label: `retention/${projectId}`, + } +} + +async function versions(fileId: string) { + return db + .select({ version: workspaceFileVersion.version }) + .from(workspaceFileVersion) + .where(eq(workspaceFileVersion.fileId, fileId)) + .orderBy(workspaceFileVersion.version) +} + +async function cleanupEvents(projectId: string) { + return db + .select({ payload: outboxEvent.payload }) + .from(outboxEvent) + .where( + and( + eq(outboxEvent.eventType, 'workspace-file.storage.cleanup'), + sql`${outboxEvent.payload}->>'key' LIKE ${`project/${projectId}/%`}` + ) + ) +} + +describe('Project file retention follows the current payer in PostgreSQL', () => { + check( + 'scheduled discovery includes shared Projects with their payer policy independently of environment chunks', + async () => { + const f = await fixture() + const discovered: CleanupJobPayload[] = [] + await runCleanupWithLimits('cleanup-soft-deletes', { files: 1 }, async (scope) => { + discovered.push(scope) + }) + const scopes = discovered.filter((scope) => scope.projectIds?.includes(f.projectId)) + expect(scopes).toHaveLength(1) + expect(scopes[0]).toMatchObject({ plan: 'free', retentionHours: 30 * 24, workspaceIds: [] }) + } + ) + + check( + 'workspace archive purge waits on current Project authority before locking file/version rows or billing', + async () => { + const f = await fixture() + const expired = await seedFile(f, 40 * 24, true, 'workspace') + await db.update(workspace).set({ storageUsedBytes: 5 }).where(eq(workspace.id, f.workspaceId)) + const acquired = createDeferred() + const release = createDeferred() + const authority = db.transaction(async (tx) => { + await authorizeProject( + tx, + createSessionPrincipal({ userId: f.ownerId }), + projectOperations.get, + { projectId: f.projectId } + ) + await tx + .select({ id: workspace.id }) + .from(workspace) + .where(eq(workspace.id, f.workspaceId)) + .for('share') + const result = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + acquired.resolve(result[0].pid) + await release.promise + }) + const authPid = await Promise.race([ + acquired.promise, + authority.then(() => { + throw new Error('Authority ended before publishing its lock') + }), + ]) + const cleanup = runCleanupSoftDeletes( + { + workspaceIds: [f.workspaceId], + plan: 'free', + retentionHours: 30 * 24, + label: 'workspace-prelock', + }, + createCleanupBudgets({ files: 1 }) + ).then( + () => null, + (error: unknown) => error + ) + let observed: string | null = null + try { + for (let attempt = 0; attempt < 100; attempt += 1) { + const [waiting] = await control<{ wait_event: string }[]>` + SELECT wait_event FROM pg_stat_activity + WHERE ${authPid} = ANY(pg_blocking_pids(pid)) AND wait_event_type = 'Lock' + ` + if (waiting) { + observed = waiting.wait_event + break + } + await sleep(10) + } + } finally { + release.resolve() + await authority + await cleanup + } + expect( + observed, + 'purge must acquire the canonical Project mutex before workspace resource locks' + ).toBe('advisory') + expect(await cleanup).toBeNull() + expect(await versions(expired.fileId)).toEqual([]) + const [usage] = await db + .select({ bytes: userStats.storageUsedBytes }) + .from(userStats) + .where(eq(userStats.userId, f.ownerId)) + expect(usage.bytes).toBe(10) + } + ) + + check( + 'free history is pruned with its ten-version floor while a paid Project ignores a stale free queue policy and uploader plan', + async () => { + const free = await fixture() + const paid = await fixture('pro') + const freeFile = await seedFile(free, 60 * 24) + const paidFile = await seedFile(paid, 60 * 24) + await runCleanupFileVersions(payload(free.projectId)) + await runCleanupFileVersions(payload(paid.projectId)) + expect(await versions(freeFile.fileId)).toEqual( + Array.from({ length: 10 }, (_, index) => ({ version: index + 3 })) + ) + expect(await versions(paidFile.fileId)).toHaveLength(12) + expect((await cleanupEvents(free.projectId)).map((row) => row.payload)).toEqual( + expect.arrayContaining( + freeFile.keys.slice(0, 2).map((key) => ({ key, context: 'project' })) + ) + ) + expect(await cleanupEvents(paid.projectId)).toEqual([]) + const [usage] = await db + .select({ bytes: userStats.storageUsedBytes }) + .from(userStats) + .where(eq(userStats.userId, free.ownerId)) + expect(usage.bytes).toBe(15) + } + ) + + check( + 'enterprise Project history uses the organization setting and never an environment override or stale queued cutoff', + async () => { + const f = await fixture('enterprise') + const kept = await seedFile(f, 48) + const expired = await seedFile(f, 200) + await runCleanupFileVersions(payload(f.projectId)) + expect(await versions(expired.fileId)).toHaveLength(10) + expect(await versions(kept.fileId)).toHaveLength(12) + if (!f.organizationId) throw new Error('Expected an organization fixture') + await db + .update(organization) + .set({ dataRetentionSettings: { fileVersionRetentionHours: null } }) + .where(eq(organization.id, f.organizationId)) + const indefinite = await seedFile(f, 1000) + await runCleanupFileVersions(payload(f.projectId)) + expect(await versions(indefinite.fileId)).toHaveLength(12) + } + ) + + check( + 'archive expiry removes exact heads and history once, debits only head bytes, and commits Project cleanup objects durably', + async () => { + const f = await fixture() + const expired = await seedFile(f, 40 * 24, true) + await runCleanupSoftDeletes(payload(f.projectId)) + await runCleanupSoftDeletes(payload(f.projectId)) + expect( + await db + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where(eq(workspaceFiles.id, expired.fileId)) + ).toEqual([]) + expect(await versions(expired.fileId)).toEqual([]) + const [usage] = await db + .select({ bytes: userStats.storageUsedBytes }) + .from(userStats) + .where(eq(userStats.userId, f.ownerId)) + expect(usage.bytes).toBe(10) + const events = await cleanupEvents(f.projectId) + expect(new Set(events.map((row) => (row.payload as { key: string }).key))).toEqual( + new Set(expired.keys) + ) + expect( + events.every((row) => (row.payload as { context: string }).context === 'project') + ).toBe(true) + } + ) + + check( + 'folder expiry preserves and renames surviving Project children when the root already contains their names', + async () => { + const f = await fixture() + const parentId = generateId() + const childId = generateId() + const rootFolderId = generateId() + const owner = { + entityType: 'project' as const, + entityId: f.projectId, + userId: f.creatorId, + resourceType: 'file' as const, + } + await db.insert(folder).values([ + { + ...owner, + id: parentId, + name: 'Archived parent', + deletedAt: new Date(Date.now() - 40 * 24 * HOUR), + }, + { ...owner, id: rootFolderId, name: 'Architecture' }, + ]) + await db.insert(folder).values({ ...owner, id: childId, name: 'Architecture', parentId }) + const file = await seedFile(f, 1) + const rootFile = await seedFile(f, 1) + await db + .update(workspaceFiles) + .set({ folderId: parentId, originalName: 'README.md' }) + .where(eq(workspaceFiles.id, file.fileId)) + await db + .update(workspaceFiles) + .set({ originalName: 'README.md' }) + .where(eq(workspaceFiles.id, rootFile.fileId)) + await runCleanupSoftDeletes(payload(f.projectId), createCleanupBudgets({ folders: 1 })) + expect( + await db.select({ id: folder.id }).from(folder).where(eq(folder.id, parentId)) + ).toEqual([]) + const [child] = await db + .select({ parentId: folder.parentId, name: folder.name }) + .from(folder) + .where(eq(folder.id, childId)) + expect(child).toEqual({ parentId: null, name: 'Architecture (1)' }) + const [survivor] = await db + .select({ folderId: workspaceFiles.folderId, name: workspaceFiles.originalName }) + .from(workspaceFiles) + .where(eq(workspaceFiles.id, file.fileId)) + expect(survivor.folderId).toBeNull() + expect(survivor.name).not.toBe('README.md') + expect(survivor.name).toMatch(/^README/) + expect(await versions(file.fileId)).toHaveLength(12) + expect(await cleanupEvents(f.projectId)).toEqual([]) + } + ) + + check( + 'a failed archive deletion rolls back current bytes, version removal, and the cleanup outbox', + async () => { + const f = await fixture() + const expired = await seedFile(f, 40 * 24, true) + const trigger = `retention_failure_${generateId().replaceAll('-', '')}` + await db.execute( + sql.raw(`CREATE FUNCTION ${trigger}() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN + IF OLD.id = '${expired.fileId}' THEN RAISE EXCEPTION 'retention rollback fixture'; END IF; RETURN OLD; END $$`) + ) + await db.execute( + sql.raw( + `CREATE TRIGGER ${trigger} BEFORE DELETE ON workspace_files FOR EACH ROW EXECUTE FUNCTION ${trigger}()` + ) + ) + try { + await expect( + runCleanupSoftDeletes(payload(f.projectId), createCleanupBudgets({ files: 1 })) + ).rejects.toMatchObject({ cause: { message: 'retention rollback fixture' } }) + expect(await versions(expired.fileId)).toHaveLength(12) + expect(await cleanupEvents(f.projectId)).toEqual([]) + const [usage] = await db + .select({ bytes: userStats.storageUsedBytes }) + .from(userStats) + .where(eq(userStats.userId, f.ownerId)) + expect(usage.bytes).toBe(15) + } finally { + await db.execute(sql.raw(`DROP TRIGGER ${trigger} ON workspace_files`)) + await db.execute(sql.raw(`DROP FUNCTION ${trigger}()`)) + } + } + ) +}) + +afterAll(async () => { + try { + for (const f of fixtures) { + await db + .delete(outboxEvent) + .where( + sql`${outboxEvent.payload}->>'key' LIKE ${`project/${f.projectId}/%`} OR ${outboxEvent.payload}->>'key' LIKE ${`workspace/${f.workspaceId}/%`}` + ) + await db + .delete(workspaceFiles) + .where( + and(eq(workspaceFiles.entityType, 'project'), eq(workspaceFiles.entityId, f.projectId)) + ) + await db + .delete(folder) + .where(and(eq(folder.entityType, 'project'), eq(folder.entityId, f.projectId))) + await deleteWorkspaceFixture(db, eq(workspace.id, f.workspaceId)) + await db + .delete(subscription) + .where( + inArray(subscription.referenceId, [ + f.ownerId, + f.creatorId, + ...(f.organizationId ? [f.organizationId] : []), + ]) + ) + if (f.organizationId) + await db.delete(organization).where(eq(organization.id, f.organizationId)) + await db.delete(user).where(inArray(user.id, [f.ownerId, f.creatorId])) + } + } finally { + resetEnvFlagsMock() + await control.end({ timeout: 1 }) + const path = process.env.PROJECT_FILE_RETENTION_REPORT_PATH + if (path) { + await mkdir(dirname(path), { recursive: true }) + await writeFile(path, JSON.stringify({ checks }, null, 2)) + } + } +}) diff --git a/apps/sim/lib/projects/files/application/document-lifecycle.ts b/apps/sim/lib/projects/files/application/document-lifecycle.ts new file mode 100644 index 00000000000..322b9c566f1 --- /dev/null +++ b/apps/sim/lib/projects/files/application/document-lifecycle.ts @@ -0,0 +1,32 @@ +import { isRecordLike } from '@sim/utils/object' +import type { OutboxHandlerRegistry } from '@/lib/core/outbox/service' +import { retireLiveProjectFileDoc } from '@/lib/realtime/notify' + +const RETIRE_DOCUMENT_EVENT = 'project-file.document.retire' + +export const projectFileDocumentOutboxHandlers = { + [RETIRE_DOCUMENT_EVENT]: async (payload, context) => { + if ( + !isRecordLike(payload) || + typeof payload.projectId !== 'string' || + !payload.projectId || + typeof payload.fileId !== 'string' || + !payload.fileId || + typeof payload.retiredDocId !== 'string' || + !payload.retiredDocId || + typeof payload.replacementDocId !== 'string' || + !payload.replacementDocId + ) + throw new Error('Invalid Project document retirement') + context.signal.throwIfAborted() + await retireLiveProjectFileDoc( + { + projectId: payload.projectId, + fileId: payload.fileId, + retiredDocId: payload.retiredDocId, + replacementDocId: payload.replacementDocId, + }, + context.signal + ) + }, +} satisfies OutboxHandlerRegistry diff --git a/apps/sim/lib/projects/files/prefix-cleanup.ts b/apps/sim/lib/projects/files/prefix-cleanup.ts new file mode 100644 index 00000000000..30ba90a870f --- /dev/null +++ b/apps/sim/lib/projects/files/prefix-cleanup.ts @@ -0,0 +1,315 @@ +import { opendir } from 'node:fs/promises' +import { join } from 'node:path' +import { db } from '@sim/db' +import { project, uploadSession } from '@sim/db/schema' +import { describeError } from '@sim/utils/errors' +import { isRecordLike } from '@sim/utils/object' +import { and, asc, eq, gt, inArray, isNull, sql } from 'drizzle-orm' +import { + continueOutboxHandler, + enqueueOutboxEvent, + type OutboxHandler, + type OutboxHandlerRegistry, +} from '@/lib/core/outbox/service' +import type { DbTransaction } from '@/lib/db/types' +import { + getStorageConfig, + USE_BLOB_STORAGE, + USE_GCS_STORAGE, + USE_S3_STORAGE, +} from '@/lib/uploads/config' +import { isObjectNotFoundError } from '@/lib/uploads/core/errors' +import { UPLOAD_DIR_SERVER } from '@/lib/uploads/core/setup.server' +import { deleteFile } from '@/lib/uploads/core/storage-service' +import { abortProviderUpload, UPLOAD_URL_TTL_MS } from '@/lib/uploads/upload-session/provider' +import type { UploadSessionRecord } from '@/lib/uploads/upload-session/service' +import { PROJECT_FILE_UPLOAD_BINDING_KEY } from '@/lib/uploads/upload-session/types' + +const EVENT_TYPE = 'project-file.storage.prefix-cleanup' +const PAGE_SIZE = 500 +const CLOCK_SKEW_MS = 60_000 + +type UploadCleanup = Pick< + UploadSessionRecord, + 'id' | 'method' | 'storageProvider' | 'providerUploadId' | 'finalKey' +> +interface CleanupPayload { + projectId: string + uploads?: UploadCleanup[] +} + +/** No transfer can be exposed after fresh admission fails, so retirement bounds its last signature. */ +function projectUploadCleanupAvailableAt(now = new Date()): Date { + return new Date(now.getTime() + UPLOAD_URL_TTL_MS + CLOCK_SKEW_MS) +} + +function prefixFor(projectId: string): string { + if (!/^[a-zA-Z0-9_-]+$/.test(projectId)) throw new Error('Invalid Project cleanup identity') + return `project/${projectId}/` +} + +function parsePayload(value: unknown): CleanupPayload { + if (!isRecordLike(value) || typeof value.projectId !== 'string') + throw new Error('Invalid Project cleanup payload') + const prefix = prefixFor(value.projectId) + if (value.uploads === undefined) return { projectId: value.projectId } + if (!Array.isArray(value.uploads) || value.uploads.length > PAGE_SIZE) + throw new Error('Invalid upload cleanup batch') + const uploads = value.uploads.map((upload): UploadCleanup => { + if ( + !isRecordLike(upload) || + typeof upload.id !== 'string' || + !/^[a-zA-Z0-9_-]+$/.test(upload.id) || + (upload.method !== 'put' && upload.method !== 'multipart') || + (upload.storageProvider !== 'local' && + upload.storageProvider !== 's3' && + upload.storageProvider !== 'blob' && + upload.storageProvider !== 'gcs') || + (upload.providerUploadId !== null && typeof upload.providerUploadId !== 'string') || + typeof upload.finalKey !== 'string' || + !upload.finalKey.startsWith(prefix) + ) + throw new Error('Invalid retired upload descriptor') + return { + id: upload.id, + method: upload.method, + storageProvider: upload.storageProvider, + providerUploadId: upload.providerUploadId, + finalKey: upload.finalKey, + } + }) + return { projectId: value.projectId, uploads } +} + +async function enqueueSweeps( + executor: Pick, + payload: CleanupPayload, + retiredAt = new Date() +) { + await enqueueOutboxEvent(executor, EVENT_TYPE, payload) + await enqueueOutboxEvent(executor, EVENT_TYPE, payload, { + availableAt: projectUploadCleanupAvailableAt(retiredAt), + }) +} + +/** Must follow Project and payer locks and precede directory/file locks in the retirement transaction. */ +export async function retireProjectUploadsInTx( + tx: DbTransaction, + projectId: string +): Promise { + prefixFor(projectId) + const now = new Date() + let afterId = '' + let queued = false + for (;;) { + const rows = await tx + .select() + .from(uploadSession) + .where( + and( + eq(uploadSession.purpose, 'project_file'), + sql`${uploadSession.metadata}->${PROJECT_FILE_UPLOAD_BINDING_KEY}->>'entityId' = ${projectId}`, + isNull(uploadSession.completedFileId), + gt(uploadSession.id, afterId) + ) + ) + .orderBy(asc(uploadSession.id)) + .limit(PAGE_SIZE) + .for('update') + if (!rows.length) break + const uploads = rows.map( + (row): UploadCleanup => ({ + id: row.id, + method: row.method, + storageProvider: row.storageProvider, + providerUploadId: row.providerUploadId, + finalKey: row.finalKey, + }) + ) + parsePayload({ projectId, uploads }) + await tx + .update(uploadSession) + .set({ + status: 'aborting', + expiresAt: now, + processingLeaseId: null, + processingLeaseExpiresAt: null, + updatedAt: now, + }) + .where( + inArray( + uploadSession.id, + rows.map((row) => row.id) + ) + ) + await enqueueSweeps(tx, { projectId, uploads }, now) + queued = true + afterId = rows[rows.length - 1].id + } + if (!queued) await enqueueSweeps(tx, { projectId }, now) +} + +/** Late provider responses schedule their own durable sweep after the owner has disappeared. */ +export async function queueRetiredProjectUploadCleanup( + session: UploadSessionRecord +): Promise { + if (session.purpose !== 'project_file') return false + const binding = session.metadata[PROJECT_FILE_UPLOAD_BINDING_KEY] + if (!isRecordLike(binding) || typeof binding.entityId !== 'string') + throw new Error('Invalid Project upload binding') + const payload = parsePayload({ + projectId: binding.entityId, + uploads: [ + { + id: session.id, + method: session.method, + storageProvider: session.storageProvider, + providerUploadId: session.providerUploadId, + finalKey: session.finalKey, + }, + ], + }) + return db.transaction(async (tx) => { + const [owner] = await tx + .select({ id: project.id }) + .from(project) + .where(eq(project.id, payload.projectId)) + if (owner) return false + await tx + .update(uploadSession) + .set({ + status: 'aborting', + expiresAt: new Date(), + processingLeaseId: null, + processingLeaseExpiresAt: null, + updatedAt: new Date(), + }) + .where( + and( + eq(uploadSession.id, session.id), + eq(uploadSession.purpose, 'project_file'), + eq(uploadSession.finalKey, session.finalKey), + isNull(uploadSession.completedFileId) + ) + ) + await enqueueSweeps(tx, payload) + return true + }) +} + +async function localKeys(prefix: string, signal: AbortSignal): Promise { + const keys: string[] = [] + async function visit(key: string): Promise { + signal.throwIfAborted() + let directory + try { + directory = await opendir(join(UPLOAD_DIR_SERVER, key)) + } catch (error) { + if (describeError(error).code === 'ENOENT') return + throw error + } + for await (const entry of directory) { + signal.throwIfAborted() + const child = `${key}${entry.name}` + if (entry.isDirectory()) await visit(`${child}/`) + else keys.push(child) + if (keys.length >= PAGE_SIZE) return + } + } + await visit(prefix) + return keys +} + +async function listKeys(prefix: string, signal: AbortSignal): Promise { + signal.throwIfAborted() + if (USE_S3_STORAGE) { + const { ListObjectsV2Command } = await import('@aws-sdk/client-s3') + const { getS3Client } = await import('@/lib/uploads/providers/s3/client') + const config = getStorageConfig('project') + const page = await getS3Client().send( + new ListObjectsV2Command({ Bucket: config.bucket, Prefix: prefix, MaxKeys: PAGE_SIZE }), + { abortSignal: signal } + ) + return (page.Contents ?? []).flatMap((object) => (object.Key ? [object.Key] : [])) + } + if (USE_BLOB_STORAGE) { + const { getBlobServiceClient } = await import('@/lib/uploads/providers/blob/client') + const config = getStorageConfig('project') + if (!config.containerName) throw new Error('Project blob container missing') + const pages = (await getBlobServiceClient()) + .getContainerClient(config.containerName) + .listBlobsFlat({ prefix, includeUncommitedBlobs: true, abortSignal: signal }) + .byPage({ maxPageSize: PAGE_SIZE }) + const page = await pages.next() + return page.done ? [] : page.value.segment.blobItems.map((object) => object.name) + } + if (USE_GCS_STORAGE) { + const { getGcsClient } = await import('@/lib/uploads/providers/google-cloud-storage/client') + const config = getStorageConfig('project') + if (!config.bucket) throw new Error('Project bucket missing') + const [files] = await (await getGcsClient()) + .bucket(config.bucket) + .getFiles({ prefix, maxResults: PAGE_SIZE, autoPaginate: false }) + signal.throwIfAborted() + return files.map((file) => file.name) + } + return localKeys(prefix, signal) +} + +const cleanupPrefix: OutboxHandler = async (raw, context) => { + const payload = parsePayload(raw) + const [owner] = await db + .select({ id: project.id }) + .from(project) + .where(eq(project.id, payload.projectId)) + // Canonical creation always generates fresh Project IDs; an extant owner is never disposable. + if (owner) throw new Error('Cannot clean storage for an existing Project') + const prefix = prefixFor(payload.projectId) + const uploads = payload.uploads ?? [] + for (const [index, upload] of uploads.entries()) { + context.signal.throwIfAborted() + try { + await abortProviderUpload({ + provider: upload.storageProvider, + method: upload.method, + providerUploadId: upload.providerUploadId, + uploadId: upload.id, + key: upload.finalKey, + context: 'project', + }) + } catch (error) { + const details = describeError(error) + if ( + details.code !== 'NoSuchUpload' && + details.name !== 'NoSuchUpload' && + !isObjectNotFoundError(error) + ) + throw error + } + if ((index + 1) % 20 === 0 || index === uploads.length - 1) + await context.checkpointPayload({ uploads: uploads.slice(index + 1) }) + if (context.deadlineAt && Date.now() + 5000 >= context.deadlineAt) { + await context.checkpointPayload({ uploads: uploads.slice(index + 1) }) + return continueOutboxHandler('Continuing retired Project upload cleanup') + } + } + const keys = await listKeys(prefix, context.signal) + let deleted = 0 + for (const key of keys) { + if (!key.startsWith(prefix)) throw new Error('Storage provider returned a foreign Project key') + context.signal.throwIfAborted() + if (deleted > 0 && context.deadlineAt && Date.now() + 5000 >= context.deadlineAt) + return continueOutboxHandler('Continuing Project storage cleanup') + try { + await deleteFile({ key, context: 'project', signal: context.signal }) + } catch (error) { + if (describeError(error).code !== 'ENOENT') throw error + } + deleted++ + } + if (keys.length >= PAGE_SIZE) return continueOutboxHandler('Continuing Project storage cleanup') +} + +export const projectFilePrefixCleanupOutboxHandlers = { + [EVENT_TYPE]: cleanupPrefix, +} satisfies OutboxHandlerRegistry diff --git a/apps/sim/lib/projects/files/purge.ts b/apps/sim/lib/projects/files/purge.ts new file mode 100644 index 00000000000..b76c8f98113 --- /dev/null +++ b/apps/sim/lib/projects/files/purge.ts @@ -0,0 +1,90 @@ +import { folder, publicShare, workspaceFiles, workspaceFileVersion } from '@sim/db/schema' +import { and, asc, eq, gt, inArray } from 'drizzle-orm' +import type { DbTransaction } from '@/lib/db/types' +import { acquireFolderMutationLock } from '@/lib/folders/locks' +import { retireProjectUploadsInTx } from '@/lib/projects/files/prefix-cleanup' +import { enqueueWorkspaceFileStorageCleanups } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' + +const PURGE_BATCH_SIZE = 500 + +/** + * Retires a Project's file tree after its lifecycle transaction locks the Project and payer + * and debits the exact retained heads. Blob deletion is durable work, never a precommit effect. + */ +export async function purgeProjectFilesInTx( + tx: DbTransaction, + projectId: string, + expectedBillableBytes: number +): Promise { + await retireProjectUploadsInTx(tx, projectId) + await acquireFolderMutationLock(tx, `project:${projectId}`, 'file') + const ownedFiles = and( + eq(workspaceFiles.entityType, 'project'), + eq(workspaceFiles.entityId, projectId), + eq(workspaceFiles.context, 'project') + ) + const eventIds: string[] = [] + let purgedBytes = 0 + let afterId = '' + for (;;) { + const files = await tx + .select({ + id: workspaceFiles.id, + key: workspaceFiles.key, + sizeBytes: workspaceFiles.sizeBytes, + }) + .from(workspaceFiles) + .where(and(ownedFiles, gt(workspaceFiles.id, afterId))) + .orderBy(asc(workspaceFiles.id)) + .limit(PURGE_BATCH_SIZE) + .for('update') + if (!files.length) break + const fileIds = files.map((file) => file.id) + const keys = new Set(files.map((file) => file.key)) + for (const file of files) { + if (file.sizeBytes === null || !Number.isSafeInteger(file.sizeBytes) || file.sizeBytes < 0) + throw new Error('Project retirement requires canonical file sizes') + purgedBytes += file.sizeBytes + if (!Number.isSafeInteger(purgedBytes)) + throw new Error('Project retirement exceeds the safe storage range') + } + const versions = await tx + .select({ key: workspaceFileVersion.key }) + .from(workspaceFileVersion) + .where(inArray(workspaceFileVersion.fileId, fileIds)) + for (const version of versions) keys.add(version.key) + for (const key of keys) { + if (!key.startsWith(`project/${projectId}/`)) + throw new Error('Project retirement encountered a foreign storage key') + } + eventIds.push(...(await enqueueWorkspaceFileStorageCleanups(tx, [...keys], 'project'))) + await tx + .delete(publicShare) + .where(and(eq(publicShare.resourceType, 'file'), inArray(publicShare.resourceId, fileIds))) + await tx.delete(workspaceFiles).where(and(ownedFiles, inArray(workspaceFiles.id, fileIds))) + afterId = files[files.length - 1].id + } + if (purgedBytes !== expectedBillableBytes) + throw new Error('Project storage changed during retirement') + const removedFolders = await tx + .delete(folder) + .where( + and( + eq(folder.entityType, 'project'), + eq(folder.entityId, projectId), + eq(folder.resourceType, 'file') + ) + ) + .returning({ id: folder.id }) + if (removedFolders.length) + await tx.delete(publicShare).where( + and( + eq(publicShare.resourceType, 'folder'), + inArray( + publicShare.resourceId, + removedFolders.map((row) => row.id) + ) + ) + ) + return eventIds +} diff --git a/apps/sim/lib/projects/files/retention.ts b/apps/sim/lib/projects/files/retention.ts new file mode 100644 index 00000000000..865a4dd2fc8 --- /dev/null +++ b/apps/sim/lib/projects/files/retention.ts @@ -0,0 +1,325 @@ +import { dbFor } from '@sim/db' +import { folder, organization, project, workspaceFiles, workspaceFileVersion } from '@sim/db/schema' +import { and, asc, count, eq, gt, inArray, isNotNull, isNull, lt, min, or, sql } from 'drizzle-orm' +import { CLEANUP_CONFIG } from '@/lib/billing/cleanup-dispatcher' +import { getPlanType } from '@/lib/billing/plan-helpers' +import { resolveProjectStorageBillingContext } from '@/lib/billing/storage/context' +import { prepareProjectStorageMutationInTx } from '@/lib/billing/storage/tracking' +import { + consumeRowBudget, + DEFAULT_DELETE_CHUNK_SIZE, + DEFAULT_MAX_BATCHES_PER_TABLE, + type RowBudget, +} from '@/lib/cleanup/batch-delete' +import { isBillingEnabled } from '@/lib/core/config/env-flags' +import { generateRestoreName } from '@/lib/core/utils/restore-name' +import type { DbTransaction } from '@/lib/db/types' +import { acquireFolderMutationLock } from '@/lib/folders/locks' +import { deduplicateFolderNameInScope } from '@/lib/folders/naming' +import { lockProject } from '@/lib/projects/membership' +import { workspaceFileNameFolderCondition } from '@/lib/uploads/contexts/workspace/workspace-file-folder-manager' +import { enqueueWorkspaceFileStorageCleanups } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' +import { + MAX_SUPERSEDED_FILE_VERSIONS, + releaseWorkspaceFileVersionsForPurgeInTx, +} from '@/lib/uploads/contexts/workspace/workspace-file-versions' +import { fileFolderOwnerCondition, fileOwnerCondition } from '@/lib/workspace-files/ownership-query' + +const cleanupDb = dbFor('cleanup') +const KEEP_SUPERSEDED = 9 +const FILES_PER_QUERY = 500 + +type FileRetentionJob = 'cleanup-file-versions' | 'cleanup-soft-deletes' + +/** Fresh owner and payer policy wins over a queued plan or an environment's retention settings. */ +async function prepareRetention(tx: DbTransaction, projectId: string, job: FileRetentionJob) { + await lockProject(tx, projectId) + const [owner] = await tx.select().from(project).where(eq(project.id, projectId)).limit(1) + if (!owner || owner.archivedAt) return null + const billing = await resolveProjectStorageBillingContext( + { projectId, ownerId: owner.ownerId, organizationId: owner.organizationId }, + tx + ) + if (isBillingEnabled && owner.organizationId && billing.plan === null) return null + const mutation = await prepareProjectStorageMutationInTx(tx, billing) + const plan = isBillingEnabled ? getPlanType(billing.plan) : 'enterprise' + const config = CLEANUP_CONFIG[job] + let hours: number | null = config.defaults[plan] + if (plan === 'enterprise' && owner.organizationId) { + const [payer] = await tx + .select({ settings: organization.dataRetentionSettings }) + .from(organization) + .where(eq(organization.id, owner.organizationId)) + .limit(1) + hours = payer?.settings?.[config.key] ?? null + } + if (hours === null) return null + if (!Number.isFinite(hours) || hours < 0) throw new Error('Invalid Project retention policy') + return { cutoff: new Date(Date.now() - hours * 60 * 60 * 1000), plan, mutation } +} + +function projectFiles(projectId: string) { + return fileOwnerCondition({ entityType: 'project', entityId: projectId }) +} + +/** Releases only superseded history, retaining the current head and the newest nine predecessors. */ +export async function cleanupProjectFileVersions( + projectId: string, + limit: number +): Promise { + let released = 0 + let afterId = '' + while (released < limit) { + const batch = await cleanupDb.transaction(async (tx) => { + const policy = await prepareRetention(tx, projectId, 'cleanup-file-versions') + if (!policy) return null + const maximum = policy.plan === 'free' ? 99 : MAX_SUPERSEDED_FILE_VERSIONS + const candidates = await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .innerJoin(workspaceFileVersion, eq(workspaceFileVersion.fileId, workspaceFiles.id)) + .where( + and( + projectFiles(projectId), + gt(workspaceFiles.id, afterId), + isNotNull(workspaceFileVersion.supersededAt) + ) + ) + .groupBy(workspaceFiles.id) + .having( + and( + gt(count(), KEEP_SUPERSEDED), + or( + lt( + min(workspaceFileVersion.supersededAt), + sql.param(policy.cutoff, workspaceFileVersion.supersededAt) + ), + gt(count(), maximum) + ) + ) + ) + .orderBy(asc(workspaceFiles.id)) + .limit(FILES_PER_QUERY) + if (!candidates.length) return null + await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where( + inArray( + workspaceFiles.id, + candidates.map((row) => row.id) + ) + ) + .orderBy(asc(workspaceFiles.id)) + .for('update') + const ranked = tx + .select({ + id: workspaceFileVersion.id, + supersededAt: workspaceFileVersion.supersededAt, + rank: sql`row_number() over (partition by ${workspaceFileVersion.fileId} order by ${workspaceFileVersion.version} desc)`.as( + 'rank' + ), + }) + .from(workspaceFileVersion) + .where( + and( + inArray( + workspaceFileVersion.fileId, + candidates.map((row) => row.id) + ), + isNotNull(workspaceFileVersion.supersededAt) + ) + ) + .as('ranked') + const versionLimit = Math.min(DEFAULT_DELETE_CHUNK_SIZE, limit - released) + const expired = await tx + .select({ id: ranked.id }) + .from(ranked) + .where( + and( + gt(ranked.rank, KEEP_SUPERSEDED), + or(lt(ranked.supersededAt, policy.cutoff), gt(ranked.rank, maximum)) + ) + ) + .limit(versionLimit) + const removed = expired.length + ? await tx + .delete(workspaceFileVersion) + .where( + and( + inArray( + workspaceFileVersion.id, + expired.map((row) => row.id) + ), + isNotNull(workspaceFileVersion.supersededAt) + ) + ) + .returning({ key: workspaceFileVersion.key }) + : [] + await enqueueWorkspaceFileStorageCleanups( + tx, + removed.map((row) => row.key), + 'project' + ) + return { + lastId: removed.length === versionLimit ? afterId : candidates[candidates.length - 1].id, + removed: removed.length, + } + }) + if (!batch) break + afterId = batch.lastId + released += batch.removed + } + return released +} + +/** Head rows, history, billed bytes, and cleanup intents commit or roll back together. */ +export async function cleanupArchivedProjectFiles( + projectId: string, + budget?: RowBudget +): Promise { + let total = 0 + for (let batch = 0; batch < DEFAULT_MAX_BATCHES_PER_TABLE && budget?.remaining !== 0; batch++) { + const removed = await cleanupDb.transaction(async (tx) => { + const policy = await prepareRetention(tx, projectId, 'cleanup-soft-deletes') + if (!policy) return 0 + await acquireFolderMutationLock(tx, `project:${projectId}`, 'file') + const rows = await tx + .select({ + id: workspaceFiles.id, + key: workspaceFiles.key, + sizeBytes: workspaceFiles.sizeBytes, + }) + .from(workspaceFiles) + .where( + and( + projectFiles(projectId), + isNotNull(workspaceFiles.deletedAt), + lt(workspaceFiles.deletedAt, policy.cutoff) + ) + ) + .orderBy(asc(workspaceFiles.id)) + .limit(Math.min(DEFAULT_DELETE_CHUNK_SIZE, budget?.remaining ?? DEFAULT_DELETE_CHUNK_SIZE)) + .for('update') + if (!rows.length) return 0 + consumeRowBudget(budget, rows.length) + let bytes = 0 + for (const row of rows) { + if ( + row.sizeBytes === null || + !Number.isSafeInteger(row.sizeBytes) || + row.sizeBytes < 0 || + !row.key.startsWith(`project/${projectId}/`) + ) + throw new Error('Project retention requires canonical file bytes and keys') + bytes += row.sizeBytes + if (!Number.isSafeInteger(bytes)) + throw new Error('Project retention storage exceeds the safe range') + } + const ids = rows.map((row) => row.id) + await releaseWorkspaceFileVersionsForPurgeInTx(tx, ids, policy.cutoff) + await enqueueWorkspaceFileStorageCleanups( + tx, + rows.map((row) => row.key), + 'project' + ) + await tx + .delete(workspaceFiles) + .where(and(projectFiles(projectId), inArray(workspaceFiles.id, ids))) + await policy.mutation.applyDelta(-bytes) + return rows.length + }) + total += removed + if (!removed) break + } + return total +} + +/** Surviving children are re-rooted under the directory lock before an expired folder is removed. */ +export async function cleanupArchivedProjectFileFolders( + projectId: string, + budget?: RowBudget +): Promise { + let total = 0 + const owner = { entityType: 'project' as const, entityId: projectId } + const folderScope = fileFolderOwnerCondition(owner) + for (let batch = 0; batch < DEFAULT_MAX_BATCHES_PER_TABLE && budget?.remaining !== 0; batch++) { + const removed = await cleanupDb.transaction(async (tx) => { + const policy = await prepareRetention(tx, projectId, 'cleanup-soft-deletes') + if (!policy) return 0 + await acquireFolderMutationLock(tx, `project:${projectId}`, 'file') + const [expired] = await tx + .select({ id: folder.id }) + .from(folder) + .where(and(folderScope, isNotNull(folder.deletedAt), lt(folder.deletedAt, policy.cutoff))) + .orderBy(asc(folder.id)) + .limit(1) + .for('update') + if (!expired) return 0 + consumeRowBudget(budget, 1) + for (;;) { + const children = await tx + .select({ id: folder.id, name: folder.name }) + .from(folder) + .where(and(folderScope, eq(folder.parentId, expired.id), isNull(folder.deletedAt))) + .orderBy(asc(folder.id)) + .limit(FILES_PER_QUERY) + .for('update') + if (!children.length) break + for (const child of children) { + const name = await deduplicateFolderNameInScope(tx, folderScope, null, child.name) + await tx + .update(folder) + .set({ parentId: null, name, updatedAt: new Date() }) + .where(and(folderScope, eq(folder.id, child.id))) + } + } + for (;;) { + const children = await tx + .select({ id: workspaceFiles.id, name: workspaceFiles.originalName }) + .from(workspaceFiles) + .where( + and( + projectFiles(projectId), + eq(workspaceFiles.folderId, expired.id), + isNull(workspaceFiles.deletedAt) + ) + ) + .orderBy(asc(workspaceFiles.id)) + .limit(FILES_PER_QUERY) + .for('update') + if (!children.length) break + for (const child of children) { + const originalName = await generateRestoreName( + child.name, + async (name) => { + const [existing] = await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where( + and( + projectFiles(projectId), + eq(workspaceFiles.originalName, name), + workspaceFileNameFolderCondition(null), + isNull(workspaceFiles.deletedAt) + ) + ) + .limit(1) + return Boolean(existing) + }, + { hasExtension: true } + ) + await tx + .update(workspaceFiles) + .set({ folderId: null, originalName, updatedAt: new Date() }) + .where(and(projectFiles(projectId), eq(workspaceFiles.id, child.id))) + } + } + await tx.delete(folder).where(and(folderScope, eq(folder.id, expired.id))) + return 1 + }) + total += removed + if (!removed) break + } + return total +} diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index ed93c8059f0..80f2c3d6c38 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -2,6 +2,11 @@ import { permissionGroup, project, projectWorkspace, workspace } from '@sim/db/s import { getPostgresErrorCode } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { and, asc, eq, inArray, isNull, ne, notInArray, sql } from 'drizzle-orm' +import { + type ChangeProjectStoragePayerParams, + type ChangeWorkspaceStoragePayerParams, + changeProjectAndWorkspaceStoragePayersInTx, +} from '@/lib/billing/storage/payer-transfer' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbOrTx, DbTransaction } from '@/lib/db/types' @@ -240,7 +245,8 @@ export async function transferWorkspaceProjects( tx: DbTransaction, workspaceIds: string[], organizationId: string | null, - ownerId?: string + ownerId?: string, + workspacePayerChanges: ChangeWorkspaceStoragePayerParams[] = [] ): Promise { if (!workspaceIds.length) return await lockProjectBackfillWrites(tx, workspaceIds) @@ -250,8 +256,11 @@ export async function transferWorkspaceProjects( .where(inArray(projectWorkspace.workspaceId, workspaceIds)) .orderBy(asc(projectWorkspace.projectId)) const selected = new Set(workspaceIds) + if (workspacePayerChanges.some((change) => !selected.has(change.workspaceId))) + throw new Error('Workspace payer changes must belong to the selected Project environments') + for (const owner of owners) await tryLockProject(tx, owner.id) + const projectChanges: ChangeProjectStoragePayerParams[] = [] for (const owner of owners) { - await tryLockProject(tx, owner.id) const members = await tx .select({ id: projectWorkspace.workspaceId }) .from(projectWorkspace) @@ -263,9 +272,10 @@ export async function transferWorkspaceProjects( ) } const [current] = await tx - .select({ organizationId: project.organizationId }) + .select({ organizationId: project.organizationId, ownerId: project.ownerId }) .from(project) .where(eq(project.id, owner.id)) + if (!current) throw new OrchestrationError('conflict', 'Project ownership changed; retry') if (current?.organizationId && current.organizationId !== organizationId) { await tx.execute(sql` UPDATE ${permissionGroup} @@ -275,15 +285,17 @@ export async function transferWorkspaceProjects( AND config->'deniedPartialAccessProjectIssues' ? ${owner.id} `) } - await tx - .update(project) - .set({ - organizationId, - ownerId, - updatedAt: new Date(), - }) - .where(eq(project.id, owner.id)) + projectChanges.push({ + projectId: owner.id, + organizationId, + ownerId: ownerId ?? current.ownerId, + expectedCurrentOwner: current, + }) } + await changeProjectAndWorkspaceStoragePayersInTx(tx, { + projectChanges, + workspaceChanges: workspacePayerChanges, + }) } /** Existing ownership paths can hold workspace rows first; refuse contention instead of inverting locks. */ diff --git a/apps/sim/lib/public-shares/share-manager.ts b/apps/sim/lib/public-shares/share-manager.ts index 1d9cd3caa09..4cf1379bcdb 100644 --- a/apps/sim/lib/public-shares/share-manager.ts +++ b/apps/sim/lib/public-shares/share-manager.ts @@ -11,6 +11,9 @@ import type { } from '@/lib/api/contracts/public-shares' import { encryptSecret } from '@/lib/core/security/encryption' import { getBaseUrl } from '@/lib/core/utils/urls' +import { lockWorkspaceProject } from '@/lib/projects/membership' +import { resolveFileOwner } from '@/lib/workspace-files/ownership' +import { fileOwnerCondition } from '@/lib/workspace-files/ownership-query' const logger = createLogger('PublicShareManager') @@ -174,80 +177,96 @@ export async function upsertFileShare({ allowedEmails, token, }: UpsertFileShareInput): Promise { - const [existing] = await db - .select() - .from(publicShare) - .where(and(eq(publicShare.resourceType, 'file'), eq(publicShare.resourceId, fileId))) - .limit(1) + return db.transaction(async (tx) => { + await lockWorkspaceProject(tx, workspaceId) + const [file] = await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where( + and( + eq(workspaceFiles.id, fileId), + fileOwnerCondition({ entityType: 'workspace', entityId: workspaceId }), + isNull(workspaceFiles.deletedAt) + ) + ) + .for('update') + if (!file) throw new ShareValidationError('File not found') + const [existing] = await tx + .select() + .from(publicShare) + .where(and(eq(publicShare.resourceType, 'file'), eq(publicShare.resourceId, fileId))) + .limit(1) - const finalAuthType: ShareAuthType = - authType ?? (existing?.authType as ShareAuthType | undefined) ?? 'public' - const existingAllowedEmails = Array.isArray(existing?.allowedEmails) - ? (existing.allowedEmails as string[]) - : [] + const finalAuthType: ShareAuthType = + authType ?? (existing?.authType as ShareAuthType | undefined) ?? 'public' + const existingAllowedEmails = Array.isArray(existing?.allowedEmails) + ? (existing.allowedEmails as string[]) + : [] - // Disabling preserves the stored config (and skips validation) so turning - // sharing off always succeeds; only enabling validates the chosen auth mode. - let finalPassword: string | null = existing?.password ?? null - let finalAllowedEmails: string[] = existingAllowedEmails - if (isActive) { - if (finalAuthType === 'password') { - if (password) { - finalPassword = (await encryptSecret(password)).encrypted - } else if (existing?.password) { - finalPassword = existing.password + // Disabling preserves the stored config (and skips validation) so turning + // sharing off always succeeds; only enabling validates the chosen auth mode. + let finalPassword: string | null = existing?.password ?? null + let finalAllowedEmails: string[] = existingAllowedEmails + if (isActive) { + if (finalAuthType === 'password') { + if (password) { + finalPassword = (await encryptSecret(password)).encrypted + } else if (existing?.password) { + finalPassword = existing.password + } else { + throw new ShareValidationError('Password is required for password-protected shares') + } + finalAllowedEmails = [] + } else if (finalAuthType === 'email' || finalAuthType === 'sso') { + finalAllowedEmails = allowedEmails ?? existingAllowedEmails + if (finalAllowedEmails.length === 0) { + throw new ShareValidationError( + 'At least one allowed email is required for email/SSO shares' + ) + } + finalPassword = null } else { - throw new ShareValidationError('Password is required for password-protected shares') + finalPassword = null + finalAllowedEmails = [] } - finalAllowedEmails = [] - } else if (finalAuthType === 'email' || finalAuthType === 'sso') { - finalAllowedEmails = allowedEmails ?? existingAllowedEmails - if (finalAllowedEmails.length === 0) { - throw new ShareValidationError( - 'At least one allowed email is required for email/SSO shares' - ) - } - finalPassword = null - } else { - finalPassword = null - finalAllowedEmails = [] } - } - const [row] = await db - .insert(publicShare) - .values({ - id: generateId(), - resourceType: 'file', - resourceId: fileId, - workspaceId, - createdBy: userId, - token: token ?? generateShortId(), - isActive, - authType: finalAuthType, - password: finalPassword, - allowedEmails: finalAllowedEmails, - }) - .onConflictDoUpdate({ - target: [publicShare.resourceType, publicShare.resourceId], - set: { + const [row] = await tx + .insert(publicShare) + .values({ + id: generateId(), + resourceType: 'file', + resourceId: fileId, + workspaceId, + entityType: 'workspace', + entityId: workspaceId, + createdBy: userId, + token: token ?? generateShortId(), isActive, authType: finalAuthType, password: finalPassword, allowedEmails: finalAllowedEmails, - updatedAt: new Date(), - }, - }) - .returning() + }) + .onConflictDoUpdate({ + target: [publicShare.resourceType, publicShare.resourceId], + set: { + isActive, + authType: finalAuthType, + password: finalPassword, + allowedEmails: finalAllowedEmails, + updatedAt: new Date(), + }, + }) + .returning() - logger.info('Upserted file share', { - fileId, - workspaceId, - isActive, - authType: finalAuthType, - token: row.token, + logger.info('Upserted file share', { + fileId, + workspaceId, + isActive, + authType: finalAuthType, + }) + return mapShareRecord(row) }) - return mapShareRecord(row) } /** @@ -257,8 +276,8 @@ export async function upsertFileShare({ * leaked through this path. */ export interface ResolvedShare { - share: PublicShareRow - file: WorkspaceFileRow + share: PublicShareRow & { workspaceId: string } + file: WorkspaceFileRow & { workspaceId: string; userId: string } /** Owning workspace name, for provenance on the public page. */ workspaceName: string | null /** Display name of the file's uploader. */ @@ -287,11 +306,22 @@ export async function resolveActiveShareByToken(token: string): Promise { const timeoutSignal = AbortSignal.timeout(APPLY_EDIT_TIMEOUT_MS) const response = await fetch(`${getSocketServerUrl()}/api/file-doc/apply-edit`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': env.INTERNAL_API_SECRET }, - body: JSON.stringify({ fileId, markdown, version: order.version }), + body: JSON.stringify({ fileId, markdown, version: order.version, ...(owner ? { owner } : {}) }), signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal, }) if (!response.ok) { @@ -227,13 +228,14 @@ export async function applyEditToLiveFileDoc( export async function invalidateLiveFileDoc( fileId: string, version: number, - signal?: AbortSignal + signal?: AbortSignal, + owner?: { entityType: 'project'; entityId: string } ): Promise { const timeoutSignal = AbortSignal.timeout(APPLY_EDIT_TIMEOUT_MS) const response = await fetch(`${getSocketServerUrl()}/api/file-doc/invalidate`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': env.INTERNAL_API_SECRET }, - body: JSON.stringify({ fileId, version }), + body: JSON.stringify({ fileId, version, ...(owner ? { owner } : {}) }), signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal, }) await response.body?.cancel().catch(() => {}) @@ -241,3 +243,21 @@ export async function invalidateLiveFileDoc( throw new Error(`Live document invalidation failed with status ${response.status}`) } } + +/** Retire only a named Project document history, so delayed delivery cannot erase a restored file. */ +export async function retireLiveProjectFileDoc( + target: { projectId: string; fileId: string; retiredDocId: string; replacementDocId: string }, + signal?: AbortSignal +): Promise { + const timeout = AbortSignal.timeout(APPLY_EDIT_TIMEOUT_MS) + // boundary-raw-fetch: The realtime relay exposes a shared-secret internal HTTP protocol. + const response = await fetch(`${getSocketServerUrl()}/api/file-doc/retire`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'x-api-key': env.INTERNAL_API_SECRET }, + body: JSON.stringify(target), + signal: signal ? AbortSignal.any([signal, timeout]) : timeout, + }) + await response.body?.cancel().catch(() => {}) + if (!response.ok) + throw new Error(`Project document retirement failed with status ${response.status}`) +} diff --git a/apps/sim/lib/uploads/config.ts b/apps/sim/lib/uploads/config.ts index ab49401e8c3..b3a8f3fcfca 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 'project': case 'table-import': case 'organization-logos': return { @@ -284,6 +285,7 @@ function getBlobConfig(context: StorageContext): StorageConfig { } case 'mothership': case 'workspace': + case 'project': 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 'project': case 'table-import': case 'organization-logos': return { bucket: GCS_CONFIG.bucket } diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.test.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.test.ts index 53fc58e0812..a2f71225829 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.test.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.test.ts @@ -1,4 +1,5 @@ import { dbChainMockFns, queueTableRows, resetDbChainMock, schemaMock } from '@sim/testing' +import { projectMembershipMock } from '@sim/testing/mocks/project-membership.mock' import { beforeEach, describe, expect, it, vi } from 'vitest' const { mockAcquireFolderMutationLock, mockDeduplicateFolderName } = vi.hoisted(() => ({ @@ -6,6 +7,8 @@ const { mockAcquireFolderMutationLock, mockDeduplicateFolderName } = vi.hoisted( mockDeduplicateFolderName: vi.fn(), })) +vi.mock('@/lib/projects/membership', () => projectMembershipMock) + vi.mock('@/lib/folders/locks', () => ({ acquireFolderMutationLock: mockAcquireFolderMutationLock, })) @@ -97,6 +100,8 @@ describe('listWorkspaceFileFolders', () => { const activeParent = { id: 'parent-1', resourceType: 'file', + entityType: null, + entityId: null, workspaceId: 'workspace-1', userId: 'user-1', name: 'Engineering', @@ -183,6 +188,8 @@ describe('relocateWorkspaceFileFolderByPath', () => { const source = { id: 'folder-source', resourceType: 'file', + entityType: null, + entityId: null, workspaceId: 'workspace-1', userId: 'user-1', name: 'xp-files', diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts index 62dd595c92b..612d8e9de34 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts @@ -7,8 +7,9 @@ import { and, eq, inArray, isNull, min, sql } from 'drizzle-orm' import { type ListSortOrder, listOrderBy } from '@/lib/api/list-query' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbOrTx, DbTransaction } from '@/lib/db/types' +import { FolderCollectionLimitExceededError } from '@/lib/folders/errors' import { acquireFolderMutationLock } from '@/lib/folders/locks' -import { deduplicateFolderName } from '@/lib/folders/naming' +import { deduplicateFolderName, deduplicateFolderNameInScope } from '@/lib/folders/naming' import { buildFolderPath, buildFolderPathIndex, @@ -20,9 +21,20 @@ import { resolveFolderMoveDestination, } from '@/lib/folders/paths' import { FOLDER_SORTS, type FolderSortBy } from '@/lib/folders/queries' +import { + isWorkspaceFolder, + requireWorkspaceFolder, + requireWorkspaceFolderCreator, +} from '@/lib/folders/scope' import { collectDescendantFolderIds } from '@/lib/folders/subtree' -import { encodeWorkspaceFileFolderDisplaySegment } from '@/lib/workspace-files/folder-display-path' +import { lockWorkspaceProject } from '@/lib/projects/membership' +import { + encodeWorkspaceFileFolderDisplaySegment, + folderPathSegments, +} from '@/lib/workspace-files/folder-display-path' import { MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS } from '@/lib/workspace-files/limits' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' +import { fileFolderOwnerCondition, fileOwnerCondition } from '@/lib/workspace-files/ownership-query' import { getWorkspaceWithOwner } from '@/lib/workspaces/permissions/utils' const logger = createLogger('WorkspaceFileFolders') @@ -36,7 +48,7 @@ const logger = createLogger('WorkspaceFileFolders') const FILE_FOLDER_RESOURCE_TYPE = 'file' as const const isFileFolder = eq(folderTable.resourceType, FILE_FOLDER_RESOURCE_TYPE) -export type WorkspaceFileFolderScope = 'active' | 'archived' | 'all' +type WorkspaceFileFolderScope = 'active' | 'archived' | 'all' /** * An {@link OrchestrationError} so every surface reaches 409 by class rather than each @@ -89,6 +101,21 @@ export interface WorkspaceFileFolderRecord { updatedAt: Date } +type FileFolderRecord = Omit< + WorkspaceFileFolderRecord, + 'workspaceId' | 'userId' +> & { + owner: O + userId: string | null + originalCreatorUserId: string | null +} + +interface FileFolderListOptions { + scope?: WorkspaceFileFolderScope + sortBy?: FolderSortBy + sortOrder?: ListSortOrder +} + export interface WorkspaceFileOperationContext { workspaceId: string workspaceOrganizationId: string | null @@ -122,7 +149,8 @@ export async function loadWorkspaceFileOperationContext( interface RawWorkspaceFileFolder { id: string workspaceId: string - userId: string + userId: string | null + originalCreatorUserId?: string | null name: string parentId: string | null sortOrder: number @@ -160,31 +188,45 @@ export async function assertWorkspaceFileItemsBelongToWorkspace(params: { fileIds?: string[] folderIds?: string[] }): Promise { + return assertFileItemsBelongToOwner({ + ...params, + owner: { entityType: 'workspace', entityId: params.workspaceId }, + }) +} + +/** Requires every selected active file and folder to belong to the canonical owner. */ +async function assertFileItemsBelongToOwner( + params: { + owner: EditableFileOwner + fileIds?: string[] + folderIds?: string[] + }, + client: DbOrTx = db +): Promise { const fileIds = Array.from(new Set(params.fileIds ?? [])) const folderIds = Array.from(new Set(params.folderIds ?? [])) const [files, folders] = await Promise.all([ fileIds.length === 0 ? Promise.resolve([]) - : db + : client .select({ id: workspaceFiles.id }) .from(workspaceFiles) .where( and( inArray(workspaceFiles.id, fileIds), - eq(workspaceFiles.workspaceId, params.workspaceId), - eq(workspaceFiles.context, 'workspace'), + fileOwnerCondition(params.owner), isNull(workspaceFiles.deletedAt) ) ), folderIds.length === 0 ? Promise.resolve([]) - : db + : client .select({ id: folderTable.id }) .from(folderTable) .where( and( inArray(folderTable.id, folderIds), - eq(folderTable.workspaceId, params.workspaceId), + fileFolderOwnerCondition(params.owner), isFileFolder, isNull(folderTable.deletedAt) ) @@ -234,9 +276,16 @@ export function workspaceFileNameFolderCondition(folderId?: string | null) { } async function acquireWorkspaceFileFolderMutationLock(tx: DbTransaction, workspaceId: string) { + await lockWorkspaceProject(tx, workspaceId) await acquireFolderMutationLock(tx, workspaceId, FILE_FOLDER_RESOURCE_TYPE) } +async function acquireFileFolderMutationLock(tx: DbTransaction, owner: EditableFileOwner) { + if (owner.entityType === 'workspace') await lockWorkspaceProject(tx, owner.entityId) + const ownerKey = owner.entityType === 'workspace' ? owner.entityId : `project:${owner.entityId}` + await acquireFolderMutationLock(tx, ownerKey, FILE_FOLDER_RESOURCE_TYPE) +} + export function buildWorkspaceFileFolderPathMap( folders: Array> ): Map { @@ -270,10 +319,28 @@ function mapFolder( folder: RawWorkspaceFileFolder, paths: Map ): WorkspaceFileFolderRecord { + return workspaceFolderRecord( + mapFileFolder(folder, { entityType: 'workspace', entityId: folder.workspaceId }, paths) + ) +} + +function workspaceFolderRecord(folder: FileFolderRecord): WorkspaceFileFolderRecord { + if (folder.owner.entityType !== 'workspace' || !folder.userId) + throw new OrchestrationError('not_found', 'Folder not found') + const { owner, originalCreatorUserId, ...record } = folder + return { ...record, userId: folder.userId, workspaceId: owner.entityId } +} + +function mapFileFolder( + folder: Omit, + owner: O, + paths: Map +): FileFolderRecord { return { id: folder.id, - workspaceId: folder.workspaceId, + owner, userId: folder.userId, + originalCreatorUserId: folder.originalCreatorUserId ?? null, name: folder.name, parentId: folder.parentId, path: paths.get(folder.id) ?? folder.name, @@ -305,7 +372,7 @@ async function getRawWorkspaceFileFolder( ) .limit(1) - return folder ?? null + return folder && isWorkspaceFolder(folder, workspaceId) ? folder : null } async function findRawWorkspaceFileFolderByName( @@ -327,7 +394,7 @@ async function findRawWorkspaceFileFolderByName( ) .limit(1) - return folder ?? null + return folder && isWorkspaceFolder(folder, workspaceId) ? folder : null } async function buildWorkspaceFileFolderPath( @@ -350,15 +417,6 @@ async function buildWorkspaceFileFolderPath( return segments.map(encodeWorkspaceFileFolderDisplaySegment).join('/') } -async function mapFolderWithPath( - workspaceId: string, - folder: RawWorkspaceFileFolder, - options?: { includeDeleted?: boolean } -): Promise { - const path = await buildWorkspaceFileFolderPath(workspaceId, folder, options) - return mapFolder(folder, new Map([[folder.id, path]])) -} - export async function getWorkspaceFileFolderPath( workspaceId: string, folderId: string, @@ -397,12 +455,13 @@ export async function findWorkspaceFileFolderIdByPath( * path map built only from archived rows truncates its path to the bare folder name. */ async function selectFileFolderPathRows( - workspaceId: string + owner: EditableFileOwner, + client: DbOrTx ): Promise>> { - return db + return client .select({ id: folderTable.id, name: folderTable.name, parentId: folderTable.parentId }) .from(folderTable) - .where(and(eq(folderTable.workspaceId, workspaceId), isFileFolder)) + .where(fileFolderOwnerCondition(owner)) } /** @@ -413,37 +472,35 @@ async function selectFileFolderPathRows( */ export async function listWorkspaceFileFolders( workspaceId: string, - options?: { - scope?: WorkspaceFileFolderScope - sortBy?: FolderSortBy - sortOrder?: ListSortOrder - } + options?: FileFolderListOptions ): Promise { + const folders = await listFileFolders({ entityType: 'workspace', entityId: workspaceId }, options) + return folders.map(workspaceFolderRecord) +} + +/** Lists folders within one canonical owner; transaction callers retain their authorization locks. */ +export async function listFileFolders( + owner: O, + options?: FileFolderListOptions, + client: DbOrTx = db +): Promise[]> { const { scope = 'active', sortBy = 'position', sortOrder = 'asc' } = options ?? {} - const rows = await db + const rows = await client .select() .from(folderTable) .where( scope === 'all' - ? and(eq(folderTable.workspaceId, workspaceId), isFileFolder) + ? fileFolderOwnerCondition(owner) : scope === 'archived' - ? and( - eq(folderTable.workspaceId, workspaceId), - isFileFolder, - sql`${folderTable.deletedAt} IS NOT NULL` - ) - : and( - eq(folderTable.workspaceId, workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) - ) + ? and(fileFolderOwnerCondition(owner), sql`${folderTable.deletedAt} IS NOT NULL`) + : and(fileFolderOwnerCondition(owner), isNull(folderTable.deletedAt)) ) .orderBy(...listOrderBy(FOLDER_SORTS[sortBy], sortOrder)) const paths = buildWorkspaceFileFolderPathMap( - scope === 'archived' ? await selectFileFolderPathRows(workspaceId) : rows + scope === 'archived' ? await selectFileFolderPathRows(owner, client) : rows ) - return rows.map((row) => mapFolder(row, paths)) + return rows.map((row) => mapFileFolder(row, owner, paths)) } export async function getWorkspaceFileFolder( @@ -470,37 +527,77 @@ export async function getWorkspaceFileFolder( return mapFolder(folder, paths) } -export async function resolveWorkspaceFileFolderTarget( - workspaceId: string, - folderId?: string | null -): Promise { - const normalized = normalizeParentId(folderId) - if (!normalized) return null +async function getFileFolder( + owner: O, + folderId: string, + client: DbOrTx +): Promise | null> { + const [row] = await client + .select() + .from(folderTable) + .where( + and( + fileFolderOwnerCondition(owner), + eq(folderTable.id, folderId), + isNull(folderTable.deletedAt) + ) + ) + .limit(1) + if (!row) return null + const paths = buildWorkspaceFileFolderPathMap(await selectFileFolderPathRows(owner, client)) + return mapFileFolder(row, owner, paths) +} - const folder = await getWorkspaceFileFolder(workspaceId, normalized) - if (!folder) { - throw new OrchestrationError('not_found', 'Target folder not found') +/** Resolves one active folder by ID or path within the authorized owner. */ +async function resolveFileFolderTarget( + owner: O, + target: { folderId?: string | null; folderPath?: string }, + client: DbOrTx = db +): Promise | null> { + if (target.folderId !== undefined && target.folderPath !== undefined) { + throw new OrchestrationError('validation', 'Specify either folderId or folderPath, not both') } - + let folderId = normalizeParentId(target.folderId) + if (target.folderPath !== undefined) { + const path = buildFolderPath(folderPathSegments(target.folderPath)) + if (path === '/') return null + const rows = await selectFileFolderPathRows(owner, client) + folderId = buildFolderPathIndex(rows).idByPath.get(path) ?? null + if (!folderId) throw new OrchestrationError('not_found', 'Target folder not found') + } + if (!folderId) return null + const folder = await getFileFolder(owner, folderId, client) + if (!folder) throw new OrchestrationError('not_found', 'Target folder not found') return folder } -export async function assertWorkspaceFileFolderTarget( +export async function resolveWorkspaceFileFolderTarget( workspaceId: string, + folderId?: string | null +): Promise { + const folder = await resolveFileFolderTarget( + { entityType: 'workspace', entityId: workspaceId }, + { folderId } + ) + if (!folder) return null + return workspaceFolderRecord(folder) +} + +/** Checks an active destination without treating a foreign folder ID as a root selection. */ +async function assertFileFolderTarget( + owner: EditableFileOwner, folderId?: string | null, - executor: DbOrTx = db + client: DbOrTx = db ): Promise { const normalized = normalizeParentId(folderId) if (!normalized) return null - - const [folder] = await executor + const [folder] = await client .select({ id: folderTable.id }) .from(folderTable) .where( and( eq(folderTable.id, normalized), - eq(folderTable.workspaceId, workspaceId), - isFileFolder, + fileFolderOwnerCondition(owner), isNull(folderTable.deletedAt) ) ) @@ -509,8 +606,20 @@ export async function assertWorkspaceFileFolderTarget( return folder.id } -export async function createWorkspaceFileFolder(params: { - workspaceId: string +export async function assertWorkspaceFileFolderTarget( + workspaceId: string, + folderId?: string | null, + executor: DbOrTx = db +): Promise { + return assertFileFolderTarget( + { entityType: 'workspace', entityId: workspaceId }, + folderId, + executor + ) +} + +interface CreateFileFolderParams { + owner: O userId: string name: string parentId?: string | null @@ -518,106 +627,93 @@ export async function createWorkspaceFileFolder(params: { exactName?: boolean /** Validates the exact post-deduplication name before the folder row is inserted. */ validateResolvedName?: (name: string) => void -}): Promise { - const requestedName = normalizeWorkspaceFileItemName(params.name, 'Folder') - - const folder = await db.transaction(async (tx) => { - await acquireWorkspaceFileFolderMutationLock(tx, params.workspaceId) - - const parentId = normalizeParentId(params.parentId) - if (parentId) { - const [target] = await tx - .select({ id: folderTable.id }) - .from(folderTable) - .where( - and( - eq(folderTable.id, parentId), - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) - ) - ) - .limit(1) +} - if (!target) { - throw new OrchestrationError('not_found', 'Target folder not found') - } - } +export async function createWorkspaceFileFolder( + params: Omit, 'owner'> & { workspaceId: string } +): Promise { + const folder = await db.transaction((tx) => + createFileFolder( + { + ...params, + owner: { entityType: 'workspace', entityId: params.workspaceId }, + }, + tx + ) + ) + return workspaceFolderRecord(folder) +} - const deduplicate = params.exactName === false - const name = deduplicate +/** Creates one folder under the caller's owner transaction and the shared hierarchy lock. */ +async function createFileFolder( + params: CreateFileFolderParams, + tx: DbTransaction +): Promise> { + const requestedName = normalizeWorkspaceFileItemName(params.name, 'Folder') + await acquireFileFolderMutationLock(tx, params.owner) + const parentId = await assertFileFolderTarget(params.owner, params.parentId, tx) + const ownerCondition = fileFolderOwnerCondition(params.owner) + const deduplicate = params.exactName === false + const name = deduplicate + ? params.owner.entityType === 'workspace' ? await deduplicateFolderName( tx, - params.workspaceId, + params.owner.entityId, parentId, requestedName, FILE_FOLDER_RESOURCE_TYPE ) - : requestedName - - params.validateResolvedName?.(name) - - if (!deduplicate) { - const existingFolders = await tx - .select({ id: folderTable.id }) - .from(folderTable) - .where( - and( - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - eq(folderTable.name, name), - folderParentCondition(parentId), - isNull(folderTable.deletedAt) - ) - ) - .limit(1) - - if (existingFolders.length > 0) { - throw new WorkspaceFileFolderConflictError(name) - } - } - - const [sortOrderResult] = await tx - .select({ minSortOrder: min(folderTable.sortOrder) }) + : await deduplicateFolderNameInScope(tx, ownerCondition, parentId, requestedName) + : requestedName + params.validateResolvedName?.(name) + if (!deduplicate) { + const existing = await tx + .select({ id: folderTable.id }) .from(folderTable) .where( and( - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, + ownerCondition, + eq(folderTable.name, name), folderParentCondition(parentId), isNull(folderTable.deletedAt) ) ) - - const id = generateId() - try { - const now = new Date() - const [inserted] = await tx - .insert(folderTable) - .values({ - id, - resourceType: FILE_FOLDER_RESOURCE_TYPE, - name, - userId: params.userId, - workspaceId: params.workspaceId, - parentId, - sortOrder: - params.sortOrder ?? - (sortOrderResult?.minSortOrder != null ? sortOrderResult.minSortOrder - 1 : 0), - createdAt: now, - updatedAt: now, - }) - .returning() - return inserted - } catch (error) { - if (getPostgresErrorCode(error) === '23505') { - throw new WorkspaceFileFolderConflictError(name) - } - throw error - } - }) - - return mapFolderWithPath(params.workspaceId, folder) + .limit(1) + if (existing.length) throw new WorkspaceFileFolderConflictError(name) + } + const [sortOrderResult] = await tx + .select({ minSortOrder: min(folderTable.sortOrder) }) + .from(folderTable) + .where(and(ownerCondition, folderParentCondition(parentId), isNull(folderTable.deletedAt))) + let inserted: typeof folderTable.$inferSelect + try { + const now = new Date() + const [row] = await tx + .insert(folderTable) + .values({ + id: generateId(), + resourceType: FILE_FOLDER_RESOURCE_TYPE, + name, + userId: params.userId, + entityType: params.owner.entityType, + entityId: params.owner.entityId, + workspaceId: params.owner.entityType === 'workspace' ? params.owner.entityId : null, + parentId, + sortOrder: + params.sortOrder ?? + (sortOrderResult?.minSortOrder != null ? sortOrderResult.minSortOrder - 1 : 0), + createdAt: now, + updatedAt: now, + }) + .returning() + if (!row) throw new OrchestrationError('internal', 'Folder creation did not return a record') + inserted = row + } catch (error) { + if (getPostgresErrorCode(error) === '23505') throw new WorkspaceFileFolderConflictError(name) + throw error + } + const paths = buildWorkspaceFileFolderPathMap(await selectFileFolderPathRows(params.owner, tx)) + return mapFileFolder(inserted, params.owner, paths) } /** @@ -674,7 +770,10 @@ export async function ensureWorkspaceFileFolderPath(params: { /** Key format: `${name}|${parentId ?? ''}` */ const folderByNameParent = new Map() for (const folder of existingFolders) { - folderByNameParent.set(`${folder.name}|${folder.parentId ?? ''}`, folder) + folderByNameParent.set( + `${folder.name}|${folder.parentId ?? ''}`, + requireWorkspaceFolder(folder, params.workspaceId) + ) } let parentId: string | null = null @@ -739,139 +838,103 @@ export async function ensureWorkspaceFileFolderPath(params: { return { folderId: parentId, createdFolderIds } } -export async function updateWorkspaceFileFolder(params: { - workspaceId: string +interface UpdateFileFolderParams { + owner: O folderId: string name?: string parentId?: string | null sortOrder?: number -}): Promise { - const folder = await db.transaction(async (tx) => { - await acquireWorkspaceFileFolderMutationLock(tx, params.workspaceId) +} + +export async function updateWorkspaceFileFolder( + params: Omit, 'owner'> & { workspaceId: string } +): Promise { + const folder = await db.transaction((tx) => + updateFileFolder( + { + ...params, + owner: { entityType: 'workspace', entityId: params.workspaceId }, + }, + tx + ) + ) + return workspaceFolderRecord(folder) +} - const [existing] = await tx - .select() +/** Renames or moves one folder while preserving its canonical owner and subtree identity. */ +async function updateFileFolder( + params: UpdateFileFolderParams, + tx: DbTransaction +): Promise> { + await acquireFileFolderMutationLock(tx, params.owner) + const ownerCondition = fileFolderOwnerCondition(params.owner) + const [existing] = await tx + .select() + .from(folderTable) + .where(and(ownerCondition, eq(folderTable.id, params.folderId), isNull(folderTable.deletedAt))) + .limit(1) + if (!existing) throw new OrchestrationError('not_found', 'Folder not found') + const finalName = + params.name === undefined + ? existing.name + : normalizeWorkspaceFileItemName(params.name, 'Folder') + const finalParentId = + params.parentId === undefined ? existing.parentId : normalizeParentId(params.parentId) + if (finalParentId === params.folderId) + throw new OrchestrationError('validation', 'Folder cannot be its own parent') + await assertFileFolderTarget(params.owner, finalParentId, tx) + if (params.parentId !== undefined) { + const activeFolders = await tx + .select({ id: folderTable.id, parentId: folderTable.parentId }) + .from(folderTable) + .where(and(ownerCondition, isNull(folderTable.deletedAt))) + .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) + assertBulkAffectedItemsWithinLimit(activeFolders.length) + if ( + finalParentId && + collectDescendantFolderIds(activeFolders, params.folderId).includes(finalParentId) + ) { + throw new OrchestrationError('validation', 'Cannot move a folder into one of its descendants') + } + } + if (finalName !== existing.name || finalParentId !== existing.parentId) { + const siblings = await tx + .select({ id: folderTable.id }) .from(folderTable) .where( and( - eq(folderTable.id, params.folderId), - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, + ownerCondition, + eq(folderTable.name, finalName), + folderParentCondition(finalParentId), isNull(folderTable.deletedAt) ) ) - .limit(1) - - if (!existing) throw new OrchestrationError('not_found', 'Folder not found') - - const updates: Partial = { updatedAt: new Date() } - const finalName = - params.name !== undefined - ? normalizeWorkspaceFileItemName(params.name, 'Folder') - : existing.name - const finalParentId = - params.parentId !== undefined ? normalizeParentId(params.parentId) : existing.parentId - - if (finalParentId === params.folderId) - throw new OrchestrationError('validation', 'Folder cannot be its own parent') - - if (finalParentId) { - const [target] = await tx - .select({ id: folderTable.id }) - .from(folderTable) - .where( - and( - eq(folderTable.id, finalParentId), - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) - ) - ) - .limit(1) - - if (!target) { - throw new OrchestrationError('not_found', 'Target folder not found') - } - } - - if (params.parentId !== undefined) { - const activeFolders = await tx - .select({ id: folderTable.id, parentId: folderTable.parentId }) - .from(folderTable) - .where( - and( - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) - ) - ) - - const descendants = collectDescendantFolderIds(activeFolders, params.folderId) - if (finalParentId && descendants.includes(finalParentId)) { - throw new OrchestrationError( - 'validation', - 'Cannot move a folder into one of its descendants' - ) - } - } - - if (finalName !== existing.name || finalParentId !== existing.parentId) { - const conflictingFolders = await tx - .select({ id: folderTable.id }) - .from(folderTable) - .where( - and( - eq(folderTable.workspaceId, params.workspaceId), - eq(folderTable.name, finalName), - folderParentCondition(finalParentId), - isFileFolder, - isNull(folderTable.deletedAt) - ) - ) - .limit(2) - - if (conflictingFolders.some((row) => row.id !== params.folderId)) { - throw new WorkspaceFileFolderConflictError(finalName) - } - } - - if (params.name !== undefined) { - updates.name = finalName - } - - if (params.parentId !== undefined) { - updates.parentId = finalParentId - } - - if (params.sortOrder !== undefined) { - updates.sortOrder = params.sortOrder - } - - try { - const [updatedFolder] = await tx - .update(folderTable) - .set(updates) - .where( - and( - eq(folderTable.id, params.folderId), - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) - ) - ) - .returning() - - if (!updatedFolder) throw new OrchestrationError('not_found', 'Folder not found') - return updatedFolder - } catch (error) { - if (getPostgresErrorCode(error) === '23505') { - throw new WorkspaceFileFolderConflictError(finalName) - } - throw error - } - }) - - return mapFolderWithPath(params.workspaceId, folder) + .limit(2) + if (siblings.some((row) => row.id !== params.folderId)) + throw new WorkspaceFileFolderConflictError(finalName) + } + const updates: Partial = { updatedAt: new Date() } + if (params.name !== undefined) updates.name = finalName + if (params.parentId !== undefined) updates.parentId = finalParentId + if (params.sortOrder !== undefined) updates.sortOrder = params.sortOrder + let updated: typeof folderTable.$inferSelect + try { + const [row] = await tx + .update(folderTable) + .set(updates) + .where( + and(ownerCondition, eq(folderTable.id, params.folderId), isNull(folderTable.deletedAt)) + ) + .returning() + if (!row) throw new OrchestrationError('not_found', 'Folder not found') + updated = row + } catch (error) { + if (getPostgresErrorCode(error) === '23505') + throw new WorkspaceFileFolderConflictError(finalName) + throw error + } + const paths = buildWorkspaceFileFolderPathMap(await selectFileFolderPathRows(params.owner, tx)) + return mapFileFolder(updated, params.owner, paths) } export async function fileNameExistsInWorkspaceFolder( @@ -903,7 +966,25 @@ export async function moveWorkspaceFileItems(params: { folderIds?: string[] targetFolderId?: string | null targetFolderPath?: string -}): Promise<{ +}) { + return db.transaction((tx) => + moveFileItems( + { ...params, owner: { entityType: 'workspace', entityId: params.workspaceId } }, + tx + ) + ) +} + +async function moveFileItems( + params: { + owner: EditableFileOwner + fileIds?: string[] + folderIds?: string[] + targetFolderId?: string | null + targetFolderPath?: string + }, + tx: DbTransaction +): Promise<{ movedFiles: number movedFolders: number movedFileIds: string[] @@ -915,223 +996,213 @@ export async function moveWorkspaceFileItems(params: { throw new OrchestrationError('validation', 'Specify a target folder id or path, not both') } - return db.transaction(async (tx) => { - await acquireWorkspaceFileFolderMutationLock(tx, params.workspaceId) + await acquireFileFolderMutationLock(tx, params.owner) - let targetFolderId = normalizeParentId(params.targetFolderId) - if (params.targetFolderPath !== undefined) { - try { - parseFolderPath(params.targetFolderPath) - } catch (error) { - throw new OrchestrationError('validation', getErrorMessage(error)) - } - const index = await loadActiveFileFolderPathIndex(tx, params.workspaceId) - const resolved = - params.targetFolderPath === '/' ? null : index.idByPath.get(params.targetFolderPath) - if (resolved === undefined) { - throw new OrchestrationError('not_found', 'Target folder not found') - } - targetFolderId = resolved + let targetFolderId = normalizeParentId(params.targetFolderId) + if (params.targetFolderPath !== undefined) { + try { + parseFolderPath(params.targetFolderPath) + } catch (error) { + throw new OrchestrationError('validation', getErrorMessage(error)) } + const index = await loadActiveFileFolderPathIndex(params.owner, tx) + const resolved = + params.targetFolderPath === '/' ? null : index.idByPath.get(params.targetFolderPath) + if (resolved === undefined) { + throw new OrchestrationError('not_found', 'Target folder not found') + } + targetFolderId = resolved + } - if (targetFolderId) { - const [target] = await tx - .select({ id: folderTable.id }) - .from(folderTable) - .where( - and( - eq(folderTable.id, targetFolderId), - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) - ) - ) - .limit(1) + if (targetFolderId) { + const [target] = await tx + .select({ id: folderTable.id }) + .from(folderTable) + .where( + and( + eq(folderTable.id, targetFolderId), + fileFolderOwnerCondition(params.owner), + isFileFolder, + isNull(folderTable.deletedAt) + ) + ) + .limit(1) - if (!target) { - throw new OrchestrationError('not_found', 'Target folder not found') - } + if (!target) { + throw new OrchestrationError('not_found', 'Target folder not found') } + } + + if (folderIds.includes(targetFolderId ?? '')) { + throw new OrchestrationError('validation', 'Cannot move a folder into itself') + } + + if (folderIds.length > 0) { + const activeFolders = await tx + .select({ id: folderTable.id, parentId: folderTable.parentId }) + .from(folderTable) + .where( + and(fileFolderOwnerCondition(params.owner), isFileFolder, isNull(folderTable.deletedAt)) + ) + .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) + + assertBulkAffectedItemsWithinLimit(activeFolders.length) + + const affectedFolderIds = new Set() - if (folderIds.includes(targetFolderId ?? '')) { - throw new OrchestrationError('validation', 'Cannot move a folder into itself') + for (const folderId of folderIds) { + const descendants = collectDescendantFolderIds(activeFolders, folderId) + affectedFolderIds.add(folderId) + for (const descendantId of descendants) affectedFolderIds.add(descendantId) + if (targetFolderId && descendants.includes(targetFolderId)) { + throw new OrchestrationError( + 'validation', + 'Cannot move a folder into one of its descendants' + ) + } } - if (folderIds.length > 0) { - const activeFolders = await tx - .select({ id: folderTable.id, parentId: folderTable.parentId }) - .from(folderTable) + assertBulkAffectedItemsWithinLimit(affectedFolderIds.size + fileIds.length) + if (affectedFolderIds.size > 0) { + const descendantFiles = await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) .where( and( - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) + inArray(workspaceFiles.folderId, [...affectedFolderIds]), + fileOwnerCondition(params.owner), + isNull(workspaceFiles.deletedAt) ) ) .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) + const affectedFileIds = new Set([...fileIds, ...descendantFiles.map((file) => file.id)]) + assertBulkAffectedItemsWithinLimit(affectedFolderIds.size + affectedFileIds.size) + } + } - assertBulkAffectedItemsWithinLimit(activeFolders.length) - - const affectedFolderIds = new Set() - - for (const folderId of folderIds) { - const descendants = collectDescendantFolderIds(activeFolders, folderId) - affectedFolderIds.add(folderId) - for (const descendantId of descendants) affectedFolderIds.add(descendantId) - if (targetFolderId && descendants.includes(targetFolderId)) { - throw new OrchestrationError( - 'validation', - 'Cannot move a folder into one of its descendants' - ) - } - } - - assertBulkAffectedItemsWithinLimit(affectedFolderIds.size + fileIds.length) - if (affectedFolderIds.size > 0) { - const descendantFiles = await tx - .select({ id: workspaceFiles.id }) + const movingFiles = + fileIds.length > 0 + ? await tx + .select({ id: workspaceFiles.id, name: workspaceFiles.originalName }) .from(workspaceFiles) .where( and( - inArray(workspaceFiles.folderId, [...affectedFolderIds]), - eq(workspaceFiles.workspaceId, params.workspaceId), - eq(workspaceFiles.context, 'workspace'), + inArray(workspaceFiles.id, fileIds), + fileOwnerCondition(params.owner), isNull(workspaceFiles.deletedAt) ) ) - .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) - const affectedFileIds = new Set([...fileIds, ...descendantFiles.map((file) => file.id)]) - assertBulkAffectedItemsWithinLimit(affectedFolderIds.size + affectedFileIds.size) - } - } + : [] - const movingFiles = - fileIds.length > 0 - ? await tx - .select({ id: workspaceFiles.id, name: workspaceFiles.originalName }) - .from(workspaceFiles) - .where( - and( - inArray(workspaceFiles.id, fileIds), - eq(workspaceFiles.workspaceId, params.workspaceId), - eq(workspaceFiles.context, 'workspace'), - isNull(workspaceFiles.deletedAt) - ) - ) - : [] - - const movingFolders = - folderIds.length > 0 - ? await tx - .select({ id: folderTable.id, name: folderTable.name }) - .from(folderTable) - .where( - and( - inArray(folderTable.id, folderIds), - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) - ) + const movingFolders = + folderIds.length > 0 + ? await tx + .select({ id: folderTable.id, name: folderTable.name }) + .from(folderTable) + .where( + and( + inArray(folderTable.id, folderIds), + fileFolderOwnerCondition(params.owner), + isFileFolder, + isNull(folderTable.deletedAt) ) - : [] + ) + : [] - const movingFileIds = new Set(movingFiles.map((file) => file.id)) - const movingFolderIds = new Set(movingFolders.map((folder) => folder.id)) - const missingFileIds = [...new Set(fileIds)].filter((fileId) => !movingFileIds.has(fileId)) - const missingFolderIds = [...new Set(folderIds)].filter( - (folderId) => !movingFolderIds.has(folderId) - ) - if (missingFileIds.length > 0 || missingFolderIds.length > 0) { - throw new WorkspaceFileItemsNotFoundError(missingFileIds, missingFolderIds) - } + const movingFileIds = new Set(movingFiles.map((file) => file.id)) + const movingFolderIds = new Set(movingFolders.map((folder) => folder.id)) + const missingFileIds = [...new Set(fileIds)].filter((fileId) => !movingFileIds.has(fileId)) + const missingFolderIds = [...new Set(folderIds)].filter( + (folderId) => !movingFolderIds.has(folderId) + ) + if (missingFileIds.length > 0 || missingFolderIds.length > 0) { + throw new WorkspaceFileItemsNotFoundError(missingFileIds, missingFolderIds) + } - for (const file of movingFiles) { - const conflictingFiles = await tx - .select({ id: workspaceFiles.id }) - .from(workspaceFiles) - .where( - and( - eq(workspaceFiles.workspaceId, params.workspaceId), - eq(workspaceFiles.originalName, file.name), - eq(workspaceFiles.context, 'workspace'), - workspaceFileNameFolderCondition(targetFolderId), - isNull(workspaceFiles.deletedAt) - ) + for (const file of movingFiles) { + const conflictingFiles = await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where( + and( + fileOwnerCondition(params.owner), + eq(workspaceFiles.originalName, file.name), + workspaceFileNameFolderCondition(targetFolderId), + isNull(workspaceFiles.deletedAt) ) - .limit(2) + ) + .limit(2) - if (conflictingFiles.some((row) => row.id !== file.id)) { - throw new WorkspaceFileMoveConflictError(file.name) - } + if (conflictingFiles.some((row) => row.id !== file.id)) { + throw new WorkspaceFileMoveConflictError(file.name) } + } - const movingFolderNameCounts = new Map() - for (const folder of movingFolders) { - movingFolderNameCounts.set(folder.name, (movingFolderNameCounts.get(folder.name) ?? 0) + 1) - const conflictingFolders = await tx - .select({ id: folderTable.id }) - .from(folderTable) - .where( - and( - eq(folderTable.workspaceId, params.workspaceId), - eq(folderTable.name, folder.name), - folderParentCondition(targetFolderId), - isFileFolder, - isNull(folderTable.deletedAt) - ) + const movingFolderNameCounts = new Map() + for (const folder of movingFolders) { + movingFolderNameCounts.set(folder.name, (movingFolderNameCounts.get(folder.name) ?? 0) + 1) + const conflictingFolders = await tx + .select({ id: folderTable.id }) + .from(folderTable) + .where( + and( + fileFolderOwnerCondition(params.owner), + eq(folderTable.name, folder.name), + folderParentCondition(targetFolderId), + isFileFolder, + isNull(folderTable.deletedAt) ) - .limit(2) + ) + .limit(2) - if (conflictingFolders.some((row) => row.id !== folder.id)) { - throw new WorkspaceFileFolderConflictError(folder.name) - } + if (conflictingFolders.some((row) => row.id !== folder.id)) { + throw new WorkspaceFileFolderConflictError(folder.name) } + } - for (const [name, count] of movingFolderNameCounts) { - if (count > 1) { - throw new WorkspaceFileFolderConflictError(name) - } + for (const [name, count] of movingFolderNameCounts) { + if (count > 1) { + throw new WorkspaceFileFolderConflictError(name) } + } - const movedFiles = - fileIds.length > 0 - ? await tx - .update(workspaceFiles) - .set({ folderId: targetFolderId, updatedAt: new Date() }) - .where( - and( - inArray(workspaceFiles.id, fileIds), - eq(workspaceFiles.workspaceId, params.workspaceId), - eq(workspaceFiles.context, 'workspace'), - isNull(workspaceFiles.deletedAt) - ) + const movedFiles = + fileIds.length > 0 + ? await tx + .update(workspaceFiles) + .set({ folderId: targetFolderId, updatedAt: new Date() }) + .where( + and( + inArray(workspaceFiles.id, fileIds), + fileOwnerCondition(params.owner), + isNull(workspaceFiles.deletedAt) ) - .returning({ id: workspaceFiles.id }) - : [] + ) + .returning({ id: workspaceFiles.id }) + : [] - const movedFolders = - folderIds.length > 0 - ? await tx - .update(folderTable) - .set({ parentId: targetFolderId, updatedAt: new Date() }) - .where( - and( - inArray(folderTable.id, folderIds), - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) - ) + const movedFolders = + folderIds.length > 0 + ? await tx + .update(folderTable) + .set({ parentId: targetFolderId, updatedAt: new Date() }) + .where( + and( + inArray(folderTable.id, folderIds), + fileFolderOwnerCondition(params.owner), + isFileFolder, + isNull(folderTable.deletedAt) ) - .returning({ id: folderTable.id }) - : [] + ) + .returning({ id: folderTable.id }) + : [] - return { - movedFiles: movedFiles.length, - movedFolders: movedFolders.length, - movedFileIds: movedFiles.map((file) => file.id), - movedFolderIds: movedFolders.map((folder) => folder.id), - } - }) + return { + movedFiles: movedFiles.length, + movedFolders: movedFolders.length, + movedFileIds: movedFiles.map((file) => file.id), + movedFolderIds: movedFolders.map((folder) => folder.id), + } } export async function restoreWorkspaceFileFolder( @@ -1142,281 +1213,305 @@ export async function restoreWorkspaceFileFolder( if (!ws || ws.archivedAt) { throw new OrchestrationError('validation', 'Cannot restore folder into an archived workspace') } + const result = await db.transaction((tx) => + restoreFileFolder({ entityType: 'workspace', entityId: workspaceId }, folderId, tx) + ) + return { folder: workspaceFolderRecord(result.folder), restoredItems: result.restoredItems } +} - const { restored, restoredItems } = await db.transaction(async (tx) => { - await acquireWorkspaceFileFolderMutationLock(tx, workspaceId) +/** Restores the folder's archive batch, keeping independently archived descendants unchanged. */ +async function restoreFileFolder( + owner: O, + folderId: string, + tx: DbTransaction +) { + await acquireFileFolderMutationLock(tx, owner) + + const raw = await tx + .select() + .from(folderTable) + .where(and(eq(folderTable.id, folderId), fileFolderOwnerCondition(owner), isFileFolder)) + .limit(1) + .then((rows) => rows[0] ?? null) - const raw = await tx - .select() + if (!raw) { + throw new OrchestrationError('not_found', 'Folder not found') + } + if (!raw.deletedAt) throw new OrchestrationError('validation', 'Folder is not archived') + + const folderDeletedAt = raw.deletedAt + + // If the parent folder is still archived, restore to root so the folder + // doesn't become an orphan (hidden under an archived parent). + let resolvedParentId = raw.parentId + if (resolvedParentId) { + const parent = await tx + .select({ deletedAt: folderTable.deletedAt }) .from(folderTable) .where( - and(eq(folderTable.id, folderId), eq(folderTable.workspaceId, workspaceId), isFileFolder) + and(eq(folderTable.id, resolvedParentId), fileFolderOwnerCondition(owner), isFileFolder) ) .limit(1) .then((rows) => rows[0] ?? null) + if (!parent || parent.deletedAt) resolvedParentId = null + } - if (!raw) throw new OrchestrationError('not_found', 'Folder not found') - if (!raw.deletedAt) throw new OrchestrationError('validation', 'Folder is not archived') + // Clearing `deletedAt` below brings this row back under the partial unique index on + // active (workspaceId, resourceType, parentId, name). The caller cannot rename an + // archived folder, so a sibling that took the name while this folder was gone would + // otherwise make it permanently unrestorable — dedupe instead of erroring. Deduped + // against the RESOLVED parent, which re-roots when the original parent is still + // archived. Only the restore root can collide: descendants come back alongside the + // exact siblings they were archived with. + const restoredName = await deduplicateFolderNameInScope( + tx, + fileFolderOwnerCondition(owner), + resolvedParentId, + raw.name + ) + if (restoredName !== raw.name) { + logger.info('Renamed file folder on restore to avoid a sibling name conflict', { + owner, + folderId, + from: raw.name, + to: restoredName, + }) + } - const folderDeletedAt = raw.deletedAt + const stats: WorkspaceFileArchiveResult = { folders: 0, files: 0 } + const restoredFileIds: string[] = [] + const seen = new Set() + const restoreFolderSubtree = async (currentFolderId: string): Promise => { + if (seen.has(currentFolderId)) return + seen.add(currentFolderId) - // If the parent folder is still archived, restore to root so the folder - // doesn't become an orphan (hidden under an archived parent). - let resolvedParentId = raw.parentId - if (resolvedParentId) { - const parent = await tx - .select({ deletedAt: folderTable.deletedAt }) - .from(folderTable) - .where( - and( - eq(folderTable.id, resolvedParentId), - eq(folderTable.workspaceId, workspaceId), - isFileFolder - ) + const pendingFiles = await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where( + and( + fileOwnerCondition(owner), + eq(workspaceFiles.folderId, currentFolderId), + eq(workspaceFiles.deletedAt, folderDeletedAt) ) - .limit(1) - .then((rows) => rows[0] ?? null) - if (!parent || parent.deletedAt) resolvedParentId = null - } - - // Clearing `deletedAt` below brings this row back under the partial unique index on - // active (workspaceId, resourceType, parentId, name). The caller cannot rename an - // archived folder, so a sibling that took the name while this folder was gone would - // otherwise make it permanently unrestorable — dedupe instead of erroring. Deduped - // against the RESOLVED parent, which re-roots when the original parent is still - // archived. Only the restore root can collide: descendants come back alongside the - // exact siblings they were archived with. - const restoredName = await deduplicateFolderName( - tx, - workspaceId, - resolvedParentId, - raw.name, - FILE_FOLDER_RESOURCE_TYPE - ) - if (restoredName !== raw.name) { - logger.info('Renamed file folder on restore to avoid a sibling name conflict', { - workspaceId, - folderId, - from: raw.name, - to: restoredName, - }) - } - - const stats: WorkspaceFileArchiveResult = { folders: 0, files: 0 } - const seen = new Set() - const restoreFolderSubtree = async (currentFolderId: string): Promise => { - if (seen.has(currentFolderId)) return - seen.add(currentFolderId) + ) + .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS - stats.files - stats.folders + 1) + assertBulkAffectedItemsWithinLimit(stats.files + stats.folders + pendingFiles.length) + const restoredFiles = + pendingFiles.length > 0 + ? await tx + .update(workspaceFiles) + .set({ deletedAt: null, updatedAt: new Date() }) + .where( + and( + inArray( + workspaceFiles.id, + pendingFiles.map((file) => file.id) + ), + fileOwnerCondition(owner), + eq(workspaceFiles.deletedAt, folderDeletedAt) + ) + ) + .returning({ id: workspaceFiles.id }) + : [] + restoredFileIds.push(...restoredFiles.map((file) => file.id)) + stats.files += restoredFiles.length + assertBulkAffectedItemsWithinLimit(stats.files + stats.folders) - const restoredFiles = await tx - .update(workspaceFiles) - .set({ deletedAt: null, updatedAt: new Date() }) - .where( - and( - eq(workspaceFiles.folderId, currentFolderId), - eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), - eq(workspaceFiles.deletedAt, folderDeletedAt) - ) + const archivedChildren = await tx + .select({ id: folderTable.id }) + .from(folderTable) + .where( + and( + eq(folderTable.parentId, currentFolderId), + fileFolderOwnerCondition(owner), + isFileFolder, + eq(folderTable.deletedAt, folderDeletedAt) ) - .returning({ id: workspaceFiles.id }) - stats.files += restoredFiles.length - assertBulkAffectedItemsWithinLimit(stats.files + stats.folders) + ) + .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) + assertBulkAffectedItemsWithinLimit(stats.files + stats.folders + archivedChildren.length) - const archivedChildren = await tx - .select({ id: folderTable.id }) - .from(folderTable) + for (const child of archivedChildren) { + const [restoredChild] = await tx + .update(folderTable) + .set({ deletedAt: null, updatedAt: new Date() }) .where( and( - eq(folderTable.parentId, currentFolderId), - eq(folderTable.workspaceId, workspaceId), + eq(folderTable.id, child.id), + fileFolderOwnerCondition(owner), isFileFolder, eq(folderTable.deletedAt, folderDeletedAt) ) ) - .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) - assertBulkAffectedItemsWithinLimit(stats.files + stats.folders + archivedChildren.length) + .returning({ id: folderTable.id }) - for (const child of archivedChildren) { - const [restoredChild] = await tx - .update(folderTable) - .set({ deletedAt: null, updatedAt: new Date() }) - .where( - and( - eq(folderTable.id, child.id), - eq(folderTable.workspaceId, workspaceId), - isFileFolder, - eq(folderTable.deletedAt, folderDeletedAt) - ) - ) - .returning({ id: folderTable.id }) - - if (!restoredChild) continue - stats.folders += 1 - assertBulkAffectedItemsWithinLimit(stats.files + stats.folders) - await restoreFolderSubtree(child.id) - } + if (!restoredChild) continue + stats.folders += 1 + assertBulkAffectedItemsWithinLimit(stats.files + stats.folders) + await restoreFolderSubtree(child.id) } - - const [row] = await tx - .update(folderTable) - .set({ - deletedAt: null, - parentId: resolvedParentId, - name: restoredName, - updatedAt: new Date(), - }) - .where( - and(eq(folderTable.id, folderId), eq(folderTable.workspaceId, workspaceId), isFileFolder) - ) - .returning() - - stats.folders += 1 - assertBulkAffectedItemsWithinLimit(stats.files + stats.folders) - await restoreFolderSubtree(folderId) - - return { restored: row, restoredItems: stats } - }) - - logger.info('Restored workspace file folder', { workspaceId, folderId, restoredItems }) - - const allFolders = await db - .select() - .from(folderTable) - .where( - and(eq(folderTable.workspaceId, workspaceId), isFileFolder, isNull(folderTable.deletedAt)) - ) - const paths = buildWorkspaceFileFolderPathMap(allFolders) - return { - folder: mapFolder(restored, paths), - restoredItems, } + + const [row] = await tx + .update(folderTable) + .set({ + deletedAt: null, + parentId: resolvedParentId, + name: restoredName, + updatedAt: new Date(), + }) + .where(and(eq(folderTable.id, folderId), fileFolderOwnerCondition(owner), isFileFolder)) + .returning() + + if (!row) throw new OrchestrationError('not_found', 'Folder not found') + stats.folders += 1 + assertBulkAffectedItemsWithinLimit(stats.files + stats.folders) + await restoreFolderSubtree(folderId) + + const paths = buildWorkspaceFileFolderPathMap(await selectFileFolderPathRows(owner, tx)) + return { folder: mapFileFolder(row, owner, paths), restoredItems: stats, restoredFileIds } } export async function bulkArchiveWorkspaceFileItems(params: { workspaceId: string fileIds?: string[] folderIds?: string[] -}): Promise { +}) { + return db.transaction((tx) => + archiveFileItems( + { ...params, owner: { entityType: 'workspace', entityId: params.workspaceId } }, + tx + ) + ) +} + +async function archiveFileItems( + params: { + owner: EditableFileOwner + fileIds?: string[] + folderIds?: string[] + }, + tx: DbTransaction +): Promise { const now = new Date() const explicitFileIds = Array.from(new Set(params.fileIds ?? [])) const explicitFolderIds = Array.from(new Set(params.folderIds ?? [])) - return db.transaction(async (tx) => { - await acquireWorkspaceFileFolderMutationLock(tx, params.workspaceId) + await acquireFileFolderMutationLock(tx, params.owner) + await assertFileItemsBelongToOwner(params, tx) - const activeFolders = - explicitFolderIds.length > 0 - ? await tx - .select({ id: folderTable.id, parentId: folderTable.parentId }) - .from(folderTable) - .where( - and( - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) - ) - ) - .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) - : [] - assertBulkAffectedItemsWithinLimit(activeFolders.length) - const descendantFolderIds = explicitFolderIds.flatMap((folderId) => - collectDescendantFolderIds(activeFolders, folderId) - ) - const allFolderIds = Array.from(new Set([...explicitFolderIds, ...descendantFolderIds])) - assertBulkAffectedItemsWithinLimit(allFolderIds.length + explicitFileIds.length) + const activeFolders = + explicitFolderIds.length > 0 + ? await tx + .select({ id: folderTable.id, parentId: folderTable.parentId }) + .from(folderTable) + .where( + and(fileFolderOwnerCondition(params.owner), isFileFolder, isNull(folderTable.deletedAt)) + ) + .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) + : [] + assertBulkAffectedItemsWithinLimit(activeFolders.length) + const descendantFolderIds = explicitFolderIds.flatMap((folderId) => + collectDescendantFolderIds(activeFolders, folderId) + ) + const allFolderIds = Array.from(new Set([...explicitFolderIds, ...descendantFolderIds])) + assertBulkAffectedItemsWithinLimit(allFolderIds.length + explicitFileIds.length) - const descendantFiles = - allFolderIds.length > 0 - ? await tx - .select({ id: workspaceFiles.id }) - .from(workspaceFiles) - .where( - and( - inArray(workspaceFiles.folderId, allFolderIds), - eq(workspaceFiles.workspaceId, params.workspaceId), - eq(workspaceFiles.context, 'workspace'), - isNull(workspaceFiles.deletedAt) - ) + const descendantFiles = + allFolderIds.length > 0 + ? await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where( + and( + inArray(workspaceFiles.folderId, allFolderIds), + fileOwnerCondition(params.owner), + isNull(workspaceFiles.deletedAt) ) - .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) - : [] - const affectedFileIds = new Set([...explicitFileIds, ...descendantFiles.map((file) => file.id)]) - assertBulkAffectedItemsWithinLimit(allFolderIds.length + affectedFileIds.size) - - const archivedExplicitFiles = - explicitFileIds.length > 0 - ? await tx - .update(workspaceFiles) - .set({ deletedAt: now, updatedAt: now }) - .where( - and( - inArray(workspaceFiles.id, explicitFileIds), - eq(workspaceFiles.workspaceId, params.workspaceId), - eq(workspaceFiles.context, 'workspace'), - isNull(workspaceFiles.deletedAt) - ) + ) + .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) + : [] + const affectedFileIds = new Set([...explicitFileIds, ...descendantFiles.map((file) => file.id)]) + assertBulkAffectedItemsWithinLimit(allFolderIds.length + affectedFileIds.size) + + const archivedExplicitFiles = + explicitFileIds.length > 0 + ? await tx + .update(workspaceFiles) + .set({ deletedAt: now, updatedAt: now }) + .where( + and( + inArray(workspaceFiles.id, explicitFileIds), + fileOwnerCondition(params.owner), + isNull(workspaceFiles.deletedAt) ) - .returning({ id: workspaceFiles.id }) - : [] - - const archivedDescendantFiles = - allFolderIds.length > 0 - ? await tx - .update(workspaceFiles) - .set({ deletedAt: now, updatedAt: now }) - .where( - and( - inArray(workspaceFiles.folderId, allFolderIds), - eq(workspaceFiles.workspaceId, params.workspaceId), - eq(workspaceFiles.context, 'workspace'), - isNull(workspaceFiles.deletedAt) - ) + ) + .returning({ id: workspaceFiles.id }) + : [] + + const archivedDescendantFiles = + allFolderIds.length > 0 + ? await tx + .update(workspaceFiles) + .set({ deletedAt: now, updatedAt: now }) + .where( + and( + inArray(workspaceFiles.folderId, allFolderIds), + fileOwnerCondition(params.owner), + isNull(workspaceFiles.deletedAt) ) - .returning({ id: workspaceFiles.id }) - : [] + ) + .returning({ id: workspaceFiles.id }) + : [] - const archivedFolders = - allFolderIds.length > 0 - ? await tx - .update(folderTable) - .set({ deletedAt: now, updatedAt: now }) - .where( - and( - inArray(folderTable.id, allFolderIds), - eq(folderTable.workspaceId, params.workspaceId), - isFileFolder, - isNull(folderTable.deletedAt) - ) + const archivedFolders = + allFolderIds.length > 0 + ? await tx + .update(folderTable) + .set({ deletedAt: now, updatedAt: now }) + .where( + and( + inArray(folderTable.id, allFolderIds), + fileFolderOwnerCondition(params.owner), + isFileFolder, + isNull(folderTable.deletedAt) ) - .returning({ id: folderTable.id }) - : [] + ) + .returning({ id: folderTable.id }) + : [] - const archivedFileIds = Array.from( - new Set([...archivedExplicitFiles, ...archivedDescendantFiles].map((file) => file.id)) - ) - const archivedFolderIds = archivedFolders.map((folder) => folder.id) - return { - folders: archivedFolderIds.length, - files: archivedFileIds.length, - folderIds: archivedFolderIds, - fileIds: archivedFileIds, - } - }) + const archivedFileIds = Array.from( + new Set([...archivedExplicitFiles, ...archivedDescendantFiles].map((file) => file.id)) + ) + const archivedFolderIds = archivedFolders.map((folder) => folder.id) + return { + folders: archivedFolderIds.length, + files: archivedFileIds.length, + folderIds: archivedFolderIds, + fileIds: archivedFileIds, + } } -async function loadActiveFileFolderPathIndex(tx: DbOrTx, workspaceId: string) { - const rows = await tx +/** Loads a complete canonical owner tree, refusing a partial index when a bound is supplied. */ +async function loadActiveFileFolderPathIndex( + owner: EditableFileOwner, + client: DbOrTx = db, + options?: { maxRows?: number } +) { + const query = client .select() .from(folderTable) - .where( - and(eq(folderTable.workspaceId, workspaceId), isFileFolder, isNull(folderTable.deletedAt)) - ) + .where(and(fileFolderOwnerCondition(owner), isNull(folderTable.deletedAt))) + const rows = options?.maxRows === undefined ? await query : await query.limit(options.maxRows + 1) + if (options?.maxRows !== undefined && rows.length > options.maxRows) { + throw new FolderCollectionLimitExceededError('path index', options.maxRows) + } return buildFolderPathIndex(rows) } export interface WorkspaceFileFolderPathMutation { - folder: typeof folderTable.$inferSelect + folder: typeof folderTable.$inferSelect & { workspaceId: string; userId: string } path: string } @@ -1440,7 +1535,10 @@ export async function createWorkspaceFileFolderAtPath(params: { const folder = await db.transaction(async (tx) => { await acquireWorkspaceFileFolderMutationLock(tx, params.workspaceId) - const index = await loadActiveFileFolderPathIndex(tx, params.workspaceId) + const index = await loadActiveFileFolderPathIndex( + { entityType: 'workspace', entityId: params.workspaceId }, + tx + ) if (index.idByPath.has(params.path)) throw new WorkspaceFileFolderConflictError(name) const parentPath = parentFolderPath(params.path) @@ -1476,7 +1574,7 @@ export async function createWorkspaceFileFolderAtPath(params: { updatedAt: now, }) .returning() - return created + return requireWorkspaceFolderCreator(created, params.workspaceId) }) return { folder, path: params.path } @@ -1515,7 +1613,10 @@ export async function relocateWorkspaceFileFolderByPath(params: { return db.transaction(async (tx) => { await acquireWorkspaceFileFolderMutationLock(tx, params.workspaceId) - const index = await loadActiveFileFolderPathIndex(tx, params.workspaceId) + const index = await loadActiveFileFolderPathIndex( + { entityType: 'workspace', entityId: params.workspaceId }, + tx + ) const folderId = index.idByPath.get(params.path) if (!folderId) throw new OrchestrationError('not_found', 'Folder not found') /** Resolved under the lock, against the same index the collision check reads. */ @@ -1551,7 +1652,10 @@ export async function relocateWorkspaceFileFolderByPath(params: { ) .returning() if (!updated) throw new OrchestrationError('not_found', 'Folder not found') - return { folder: updated, path: destinationPath } + return { + folder: requireWorkspaceFolderCreator(updated, params.workspaceId), + path: destinationPath, + } }) } @@ -1573,7 +1677,10 @@ export async function deleteWorkspaceFileFolderByPath(params: { return db.transaction(async (tx) => { await acquireWorkspaceFileFolderMutationLock(tx, params.workspaceId) - const index = await loadActiveFileFolderPathIndex(tx, params.workspaceId) + const index = await loadActiveFileFolderPathIndex( + { entityType: 'workspace', entityId: params.workspaceId }, + tx + ) const folderId = index.idByPath.get(params.path) if (!folderId) throw new OrchestrationError('not_found', 'Folder not found') diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-live-doc-outbox.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-live-doc-outbox.ts index 5043a58f974..1670fc4ed5e 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-live-doc-outbox.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-live-doc-outbox.ts @@ -13,22 +13,75 @@ import { import { applyEditToLiveFileDoc, invalidateLiveFileDoc } from '@/lib/realtime/notify' import { downloadFile } from '@/lib/uploads/core/storage-service' import { isMarkdownFile } from '@/lib/uploads/utils/file-utils' +import { + type FileOwnerAdapters, + requireFileOwnerAdapter, +} from '@/lib/workspace-files/owner-adapters' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' +import { fileOwnerCondition } from '@/lib/workspace-files/ownership-query' export const WORKSPACE_FILE_LIVE_DOC_OUTBOX_EVENT = 'workspace-file.live-doc.reconcile' -interface WorkspaceFileLiveDocPayload { - workspaceId: string +type WorkspaceFileLiveDocPayload = { fileId: string; version: number } & ( + | { workspaceId: string; owner?: never } + | { owner: { entityType: 'project'; entityId: string }; workspaceId?: never } +) + +interface ParsedLiveDocPayload { fileId: string version: number + owner: EditableFileOwner +} + +interface LiveDocDelivery { + invalidate(target: ParsedLiveDocPayload, version: number, signal: AbortSignal): Promise + merge( + target: ParsedLiveDocPayload, + markdown: string, + signal: AbortSignal + ): ReturnType } -function parsePayload(payload: unknown): WorkspaceFileLiveDocPayload { +const LIVE_DOC_DELIVERY: FileOwnerAdapters = { + workspace: { + invalidate: (target, version, signal) => invalidateLiveFileDoc(target.fileId, version, signal), + merge: (target, markdown, signal) => + applyEditToLiveFileDoc(target.fileId, markdown, { version: target.version }, signal), + }, + project: { + invalidate: (target, version, signal) => + invalidateLiveFileDoc(target.fileId, version, signal, { + entityType: 'project', + entityId: target.owner.entityId, + }), + merge: (target, markdown, signal) => + applyEditToLiveFileDoc(target.fileId, markdown, { version: target.version }, signal, { + entityType: 'project', + entityId: target.owner.entityId, + }), + }, +} + +function parsePayload(payload: unknown): ParsedLiveDocPayload { if (!isRecordLike(payload)) { throw new Error('Workspace file live-document outbox payload must be an object') } const candidate = payload as Partial - if (typeof candidate.workspaceId !== 'string' || candidate.workspaceId.length === 0) { - throw new Error('Workspace file live-document outbox payload is missing workspaceId') + let owner: EditableFileOwner + if (candidate.owner !== undefined) { + if ( + candidate.workspaceId !== undefined || + !isRecordLike(candidate.owner) || + candidate.owner.entityType !== 'project' || + typeof candidate.owner.entityId !== 'string' || + !candidate.owner.entityId + ) + throw new Error('Invalid Project live-document owner') + owner = { entityType: 'project', entityId: candidate.owner.entityId } + } else { + if (typeof candidate.workspaceId !== 'string' || !candidate.workspaceId) + throw new Error('Workspace file live-document outbox payload is missing workspaceId') + owner = { entityType: 'workspace', entityId: candidate.workspaceId } } if (typeof candidate.fileId !== 'string' || candidate.fileId.length === 0) { throw new Error('Workspace file live-document outbox payload is missing fileId') @@ -40,7 +93,7 @@ function parsePayload(payload: unknown): WorkspaceFileLiveDocPayload { ) { throw new Error('Workspace file live-document outbox payload has an invalid version') } - return candidate as WorkspaceFileLiveDocPayload + return { fileId: candidate.fileId, version: candidate.version, owner } } const reconcileWorkspaceFileLiveDoc: OutboxHandler = async (rawPayload, context) => { @@ -58,14 +111,14 @@ const reconcileWorkspaceFileLiveDoc: OutboxHandler = async (rawPayload, .where( and( eq(workspaceFiles.id, payload.fileId), - eq(workspaceFiles.workspaceId, payload.workspaceId), - eq(workspaceFiles.context, 'workspace'), + fileOwnerCondition(payload.owner), isNull(workspaceFiles.deletedAt) ) ) .limit(1) if (!file) return + const delivery = requireFileOwnerAdapter(LIVE_DOC_DELIVERY, payload.owner) const currentVersion = file.contentUpdatedAt.getTime() if (currentVersion < payload.version) { throw new Error('Workspace file live-document reconciliation is ahead of durable content') @@ -76,24 +129,19 @@ const reconcileWorkspaceFileLiveDoc: OutboxHandler = async (rawPayload, file.sizeBytes > PASTE_LIMITS.RICH_MARKDOWN_BYTES ) { /** Later binary writes do not enqueue reconciliation, so retire the latest unsupported version. */ - await invalidateLiveFileDoc(payload.fileId, currentVersion, context.signal) + await delivery.invalidate(payload, currentVersion, context.signal) return } if (currentVersion > payload.version) return const content = await downloadFile({ key: file.key, - context: 'workspace', + context: payload.owner.entityType, maxBytes: PASTE_LIMITS.RICH_MARKDOWN_BYTES, signal: context.signal, }) context.signal.throwIfAborted() - const result = await applyEditToLiveFileDoc( - payload.fileId, - content.toString('utf-8'), - { version: payload.version }, - context.signal - ) + const result = await delivery.merge(payload, content.toString('utf-8'), context.signal) if (result.status === 'merge-unavailable') { return deferOutboxHandler('Live document merge slot is temporarily unavailable') } 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..2e9262a88aa 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -5,7 +5,13 @@ import { randomBytes } from 'crypto' import { db } from '@sim/db' -import { uploadSession, type WorkspaceFileRow, workspace, workspaceFiles } from '@sim/db/schema' +import { + folder, + uploadSession, + type WorkspaceFileRow, + workspace, + workspaceFiles, +} from '@sim/db/schema' import { createLogger } from '@sim/logger' import { sha256Hex } from '@sim/security/hash' import { @@ -48,17 +54,14 @@ 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 { - loadActiveFolderPathIndex, - resolveFolderPathFromIndex, - resolveRestoredFolderId, -} from '@/lib/folders/queries' +import { loadActiveFolderPathIndex, resolveFolderPathFromIndex } from '@/lib/folders/queries' import type { FolderIdScope } from '@/lib/folders/scope' import { normalizeVfsSegment } from '@/lib/mothership/vfs/normalize-segment' import { canonicalWorkspaceFilePath, decodeVfsPathSegments } from '@/lib/mothership/vfs/path-utils' +import { lockWorkspaceProject } from '@/lib/projects/membership' import { notifyWorkspaceFilesChanged } from '@/lib/realtime/notify' import { getServePathPrefix } from '@/lib/uploads' import type { WorkspaceFileFolderRecord } from '@/lib/uploads/contexts/workspace/workspace-file-folder-manager' @@ -101,6 +104,12 @@ 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 { + type EditableFileOwner, + type FileOwner, + resolveFileOwner, +} from '@/lib/workspace-files/ownership' +import { fileFolderOwnerCondition, fileOwnerCondition } from '@/lib/workspace-files/ownership-query' import { SIM_PAGE_CONTENT_TYPE } from '@/lib/workspace-files/page-compile' import { MAX_SIM_PAGE_UPLOAD_SNIFF_BYTES, @@ -115,6 +124,7 @@ import { fileNameExistsInWorkspaceFolder, findWorkspaceFileFolderIdByPath, getWorkspaceFileFolderPath, + listFileFolders, listWorkspaceFileFolders, normalizeWorkspaceFileItemName, resolveWorkspaceFileFolderTarget, @@ -178,6 +188,19 @@ export interface VersionedWorkspaceFileRecord extends WorkspaceFileRecord { currentVersion: number } +/** Shared file metadata never substitutes an environment ID for its owning Project. */ +interface OwnedFileRecord + extends Omit< + WorkspaceFileRecord, + 'workspaceId' | 'storageContext' | 'vfsNamespace' | 'uploadedBy' + > { + uploadedBy: string | null + originalCreatorUserId: string | null + owner: O + folderId: string | null + folderPath: string | null +} + export interface UploadedWorkspaceFileRecord extends WorkspaceFileRecord { url: string context: 'workspace' @@ -288,28 +311,43 @@ interface WorkspaceFileMetadataInsert { * created. Conflict losers receive `undefined` and must inspect the active key * in the same transaction before deciding whether the operation is idempotent. */ -async function insertWorkspaceFileMetadataInTx( +async function insertFileMetadataInTx( tx: DbOrTx, - metadata: WorkspaceFileMetadataInsert + owner: EditableFileOwner, + metadata: Omit ): Promise { + const now = new Date() const [inserted] = await tx .insert(workspaceFiles) .values({ ...omit(metadata, ['size']), + entityType: owner.entityType, + entityId: owner.entityId, + workspaceId: owner.entityType === 'workspace' ? owner.entityId : null, sizeBytes: metadata.size, - context: 'workspace', + context: owner.entityType, displayName: metadata.originalName, deletedAt: null, - uploadedAt: new Date(), - updatedAt: new Date(), - // Creation IS the first content write, so stamp the content version too (metadata writes never do). - contentUpdatedAt: new Date(), + uploadedAt: now, + updatedAt: now, + contentUpdatedAt: now, }) .onConflictDoNothing() .returning() return inserted } +async function insertWorkspaceFileMetadataInTx( + tx: DbOrTx, + metadata: WorkspaceFileMetadataInsert +): Promise { + return insertFileMetadataInTx( + tx, + { entityType: 'workspace', entityId: metadata.workspaceId }, + omit(metadata, ['workspaceId']) + ) +} + class WorkspaceFileRegistrationConflictError extends Error { constructor(key: string) { super(`Storage key ${key} is already registered to a different workspace file operation`) @@ -426,6 +464,93 @@ export async function allocateUniqueWorkspaceFileName( return withCopySuffix(baseName, generateShortId(8)) } +interface StagedFileContent { + readonly owner: Readonly + readonly key: string + readonly name: string + readonly size: number + readonly contentType: string + readonly contentHash: string | null +} + +const stagedFileContents = new WeakSet() +/** Writes private bytes before the owner-authorized metadata transaction begins. */ +async function stageFileContent(args: { + owner: EditableFileOwner + userId: string + name: string + contentType: string + content: Buffer + folderId?: string | null +}): Promise { + const name = normalizeWorkspaceFileItemName(args.name, 'File') + const owner = Object.freeze({ ...args.owner }) + const key = + owner.entityType === 'workspace' + ? generateWorkspaceFileKey(owner.entityId, name) + : `project/${owner.entityId}/${buildStorageKeySegment(`${Date.now()}-${generateShortId(16)}-`, name)}` + const staged = Object.freeze({ + owner, + key, + name, + size: args.content.length, + contentType: args.contentType, + contentHash: sha256Hex(args.content), + }) + stagedFileContents.add(staged) + try { + const uploaded = await uploadFile({ + file: args.content, + fileName: key, + contentType: args.contentType, + context: owner.entityType, + preserveKey: true, + customKey: key, + metadata: { + originalName: name, + uploadedAt: new Date().toISOString(), + purpose: owner.entityType, + userId: args.userId, + entityType: owner.entityType, + entityId: owner.entityId, + ...(owner.entityType === 'workspace' ? { workspaceId: owner.entityId } : {}), + ...(args.folderId ? { folderId: args.folderId } : {}), + }, + persistMetadata: false, + }) + if (uploaded.key !== key) throw new Error('Storage returned an unexpected file key') + } catch (error) { + await discardStagedFileContent(staged) + throw error + } + return staged +} + +function assertStagedFileOwner(owner: EditableFileOwner, staged: StagedFileContent) { + if ( + !stagedFileContents.has(staged) || + owner.entityType !== staged.owner.entityType || + owner.entityId !== staged.owner.entityId || + !staged.key.startsWith(`${owner.entityType}/${owner.entityId}/`) + ) { + throw new Error('Staged file content does not belong to this owner') + } +} + +/** Persists cleanup before attempting deletion, so storage failures remain retryable. */ +async function discardStagedFileContent(staged: StagedFileContent): Promise { + assertStagedFileOwner(staged.owner, staged) + const events = await enqueueWorkspaceFileStorageCleanups( + db, + [staged.key], + staged.owner.entityType + ) + await processWorkspaceFileStorageCleanupsNow(events, { + owner: staged.owner, + reason: 'content commit failed', + }) +} + /** * Upload a file to workspace-scoped storage */ @@ -482,40 +607,25 @@ export async function uploadWorkspaceFile( if (exactName && (await fileExistsInWorkspace(workspaceId, uniqueName, folderId))) { throw new FileConflictError(uniqueName) } - const storageKey = generateWorkspaceFileKey(workspaceId, uniqueName) const fileId = `wf_${generateShortId()}` try { - logger.info(`Generated storage key: ${storageKey}`) - - const metadata: Record = { - originalName: uniqueName, - uploadedAt: new Date().toISOString(), - purpose: 'workspace', - userId: userId, - workspaceId: workspaceId, - ...(folderId && options?.folderPath === undefined ? { folderId } : {}), - } - - const uploadResult = await uploadFile({ - file: effectiveBuffer, - fileName: storageKey, + const uploadResult = await stageFileContent({ + owner: { entityType: 'workspace', entityId: workspaceId }, + userId, + name: uniqueName, contentType: effectiveContentType, - context: 'workspace', - preserveKey: true, - customKey: storageKey, - metadata, - persistMetadata: false, + content: effectiveBuffer, + ...(options?.folderPath === undefined ? { folderId } : {}), }) - logger.info(`Upload returned key: ${uploadResult.key}`) - let finalized: { inserted: WorkspaceFileRow updatedUsage: number | undefined } try { finalized = await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, workspaceId) await acquireFolderMutationLock(tx, workspaceId, 'file') let activeFolderId: string | null if (options?.folderPath !== undefined) { @@ -557,7 +667,7 @@ export async function uploadWorkspaceFile( return { inserted, updatedUsage: usage } }) } catch (finalizationError) { - await cleanupWorkspaceStorageObject(uploadResult.key, 'metadata finalization failure') + await discardStagedFileContent(uploadResult) throw finalizationError } @@ -742,6 +852,7 @@ export async function registerUploadedWorkspaceFile(params: { size: effectiveSize, } const existing = await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, workspaceId) const found = await findWorkspaceFileByRegistrationKey(tx, key) if (!found) return undefined if (!isSameWorkspaceFileRegistration(found, registrationIdentity)) { @@ -785,6 +896,7 @@ export async function registerUploadedWorkspaceFile(params: { const displayName = await allocateUniqueWorkspaceFileName(workspaceId, effectiveName, folderId) const finalized = await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, workspaceId) await acquireFolderMutationLock(tx, workspaceId, 'file') const activeFolderId = await assertWorkspaceFileFolderTarget(workspaceId, folderId, tx) const inserted = await insertWorkspaceFileMetadataInTx(tx, { @@ -1043,6 +1155,7 @@ export async function trackChatUpload( try { if (claimable.kind === 'update') { await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, workspaceId) const updated = await tx .update(workspaceFiles) .set({ @@ -1088,6 +1201,7 @@ export async function trackChatUpload( const fileId = `wf_${generateShortId()}` await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, workspaceId) const [inserted] = await tx .insert(workspaceFiles) .values({ @@ -1152,18 +1266,39 @@ function mapWorkspaceFileRecord( workspaceId: string, folderPaths: Map ): WorkspaceFileRecord { + if (!file.userId) throw new Error('Workspace file is missing its creator') + return { + ...omit(mapFileRecord(file, { entityType: 'workspace', entityId: workspaceId }, folderPaths), [ + 'owner', + 'originalCreatorUserId', + ]), + uploadedBy: file.userId, + workspaceId: file.workspaceId || workspaceId, + } +} + +/** Projects the same persisted file metadata for either supported editable owner. */ +function mapFileRecord( + file: WorkspaceFileListRow, + owner: O, + folderPaths: Map +): OwnedFileRecord { const pathPrefix = getServePathPrefix() return { id: file.id, - workspaceId: file.workspaceId || workspaceId, + owner, name: file.originalName, key: file.key, - path: `${pathPrefix}${encodeURIComponent(file.key)}?context=workspace`, + path: + owner.entityType === 'project' + ? `/api/projects/${encodeURIComponent(owner.entityId)}/files/${encodeURIComponent(file.id)}/content` + : `${pathPrefix}${encodeURIComponent(file.key)}?context=workspace`, size: getWorkspaceFileSize(file), type: file.contentType, width: file.width, height: file.height, uploadedBy: file.userId, + originalCreatorUserId: file.originalCreatorUserId, folderId: file.folderId, folderPath: file.folderId ? (folderPaths.get(file.folderId) ?? null) : null, deletedAt: file.deletedAt, @@ -1248,18 +1383,21 @@ export async function updateWorkspaceFileDimensions( fileId: string, dimensions: { key: string; width: number; height: number } ): Promise { - const updated = await db - .update(workspaceFiles) - .set({ width: dimensions.width, height: dimensions.height }) - .where( - and( - eq(workspaceFiles.id, fileId), - eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.key, dimensions.key), - isNull(workspaceFiles.deletedAt) + const updated = await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, workspaceId) + return tx + .update(workspaceFiles) + .set({ width: dimensions.width, height: dimensions.height }) + .where( + and( + eq(workspaceFiles.id, fileId), + eq(workspaceFiles.workspaceId, workspaceId), + eq(workspaceFiles.key, dimensions.key), + isNull(workspaceFiles.deletedAt) + ) ) - ) - .returning({ id: workspaceFiles.id }) + .returning({ id: workspaceFiles.id }) + }) return updated.length > 0 } @@ -1317,8 +1455,7 @@ function workspaceFileScopeCondition( includeChatUploads?: boolean ) { const base = [ - eq(workspaceFiles.workspaceId, workspaceId), - workspaceFileContextCondition(includeChatUploads), + fileOwnerCondition({ entityType: 'workspace', entityId: workspaceId }, includeChatUploads), ] if (scope === 'all') return and(...base) return scope === 'archived' @@ -1334,6 +1471,7 @@ const workspaceFileListColumns = { id: workspaceFiles.id, key: workspaceFiles.key, userId: workspaceFiles.userId, + originalCreatorUserId: workspaceFiles.originalCreatorUserId, workspaceId: workspaceFiles.workspaceId, folderId: workspaceFiles.folderId, originalName: workspaceFiles.originalName, @@ -1398,7 +1536,7 @@ export async function listWorkspaceFiles( * Every key column is `NOT NULL`, and `id` closes each keyset so a page * boundary inside a run of equal names/sizes/timestamps is still stable. */ -const fileId = textKey(workspaceFiles.id, (row) => row.id) +const fileId = textKey(workspaceFiles.id, (row) => row.id) const WORKSPACE_FILE_SORTS = { name: [textKey(workspaceFiles.originalName, (row) => row.name), fileId], @@ -1408,7 +1546,7 @@ const WORKSPACE_FILE_SORTS = { ], uploadedAt: [timestampKey(workspaceFiles.uploadedAt, (row) => row.uploadedAt), fileId], updatedAt: [timestampKey(workspaceFiles.updatedAt, (row) => row.updatedAt), fileId], -} satisfies Record[]> +} satisfies Record[]> export interface QueryWorkspaceFilesOptions { scope?: WorkspaceFileScope @@ -1476,6 +1614,26 @@ export async function queryWorkspaceFiles( workspaceId: string, options: QueryWorkspaceFilesOptions ): Promise { + const result = await queryFileRecords({ entityType: 'workspace', entityId: workspaceId }, options) + return { + files: result.files.map((file) => { + if (!file.uploadedBy) throw new Error('Workspace file is missing its creator') + return { + ...omit(file, ['owner', 'originalCreatorUserId']), + uploadedBy: file.uploadedBy, + workspaceId, + } + }), + nextKeys: result.nextKeys, + } +} + +/** Authorized callers share one owner-filtered keyset query under their transaction. */ +async function queryFileRecords( + owner: O, + options: QueryWorkspaceFilesOptions, + client: DbOrTx = db +): Promise<{ files: OwnedFileRecord[]; nextKeys: CursorKey[] | null }> { const { scope = 'active', folderId, @@ -1489,7 +1647,7 @@ export async function queryWorkspaceFiles( if (folderId !== undefined && folderScope !== undefined) { throw new OrchestrationError('validation', 'Specify either folderId or folderScope, not both') } - const keys: readonly KeysetKey[] = WORKSPACE_FILE_SORTS[sortBy] + const keys: readonly KeysetKey[] = WORKSPACE_FILE_SORTS[sortBy] let resumeAfter: SQL | undefined if (after) { @@ -1499,14 +1657,19 @@ export async function queryWorkspaceFiles( } const conditions = [ - workspaceFileScopeCondition(workspaceId, scope), + fileOwnerCondition(owner), + scope === 'all' + ? undefined + : scope === 'archived' + ? isNotNull(workspaceFiles.deletedAt) + : isNull(workspaceFiles.deletedAt), workspaceFileFolderCondition(folderId), workspaceFileFolderScopeCondition(folderScope), searchFilter(workspaceFiles.originalName, search), resumeAfter, ] - const rows = await db + const rows = await client .select(workspaceFileListColumns) .from(workspaceFiles) .where(and(...conditions)) @@ -1514,7 +1677,12 @@ export async function queryWorkspaceFiles( .limit(limit + 1) const hasMore = rows.length > limit - const files = await hydrateWorkspaceFilePaths(rows.slice(0, limit), workspaceId) + const page = rows.slice(0, limit) + const folders = page.some((file) => file.folderId) + ? await listFileFolders(owner, { scope: 'all' }, client) + : [] + const paths = buildWorkspaceFileFolderPathMap(folders) + const files = page.map((file) => mapFileRecord(file, owner, paths)) const last = files.at(-1) return { files, nextKeys: hasMore && last ? encodeKeyset(keys, last) : null } @@ -1709,6 +1877,15 @@ export async function loadActiveWorkspaceFileContext( workspaceOrganizationId: workspace.organizationId, allowPersonalApiKeys: workspace.allowPersonalApiKeys, billedAccountUserId: workspace.billedAccountUserId, + ownership: { + entityType: workspaceFiles.entityType, + entityId: workspaceFiles.entityId, + context: workspaceFiles.context, + workspaceId: workspaceFiles.workspaceId, + organizationId: workspaceFiles.organizationId, + userId: workspaceFiles.userId, + chatId: workspaceFiles.chatId, + }, }) .from(workspaceFiles) .innerJoin(workspace, eq(workspaceFiles.workspaceId, workspace.id)) @@ -1722,7 +1899,17 @@ export async function loadActiveWorkspaceFileContext( ) .limit(1) - return context ?? null + if ( + !context || + !matchesWorkspaceFileOwner(resolveFileOwner(context.ownership), context.workspaceId) + ) { + return null + } + return omit(context, ['ownership']) +} + +function matchesWorkspaceFileOwner(owner: FileOwner | null, workspaceId: string): boolean { + return owner?.entityType === 'workspace' && owner.entityId === workspaceId } /** @@ -1741,13 +1928,28 @@ export async function loadWorkspaceFileLifecycleContext( allowPersonalApiKeys: workspace.allowPersonalApiKeys, billedAccountUserId: workspace.billedAccountUserId, deletedAt: workspaceFiles.deletedAt, + ownership: { + entityType: workspaceFiles.entityType, + entityId: workspaceFiles.entityId, + context: workspaceFiles.context, + workspaceId: workspaceFiles.workspaceId, + organizationId: workspaceFiles.organizationId, + userId: workspaceFiles.userId, + chatId: workspaceFiles.chatId, + }, }) .from(workspaceFiles) .innerJoin(workspace, eq(workspaceFiles.workspaceId, workspace.id)) .where(and(eq(workspaceFiles.id, fileId), eq(workspaceFiles.context, 'workspace'))) .limit(1) - return context ?? null + if ( + !context || + !matchesWorkspaceFileOwner(resolveFileOwner(context.ownership), context.workspaceId) + ) { + return null + } + return omit(context, ['ownership']) } /** @@ -1954,6 +2156,123 @@ export class ContentVersionConflictError extends Error { } } +interface CommitFileContentOptions { + owner: EditableFileOwner + fileId: string + staged: StagedFileContent + version: WorkspaceFileVersionWrite + expectedUpdatedAt?: Date + collabDocState?: PreparedCollabDocState + secretProvenancePolicy?: WorkspaceFileSecretProvenancePolicy +} + +/** Commits bytes, history and provenance after the caller has locked its canonical billing payer. */ +async function commitFileContentInTx(tx: DbTransaction, options: CommitFileContentOptions) { + const { owner, fileId, staged } = options + if (owner.entityType === 'workspace') await lockWorkspaceProject(tx, owner.entityId) + if (staged.contentHash === null) + throw new Error('Content replacement requires staged bytes with a verified content hash') + assertStagedFileOwner(owner, staged) + if (options.collabDocState && !options.expectedUpdatedAt) + throw new Error('Collaborative state updates require an expected content version') + const [currentFile] = await tx + .select() + .from(workspaceFiles) + .where( + and( + eq(workspaceFiles.id, fileId), + fileOwnerCondition(owner), + isNull(workspaceFiles.deletedAt) + ) + ) + .for('update') + .limit(1) + if (!currentFile) throw new OrchestrationError('not_found', 'File not found') + if ( + options.expectedUpdatedAt && + currentFile.contentUpdatedAt.getTime() !== options.expectedUpdatedAt.getTime() + ) + throw new ContentVersionConflictError(fileId) + if (options.collabDocState) await saveCollabDocStateInTx(tx, fileId, options.collabDocState) + const versionHead = await loadWorkspaceFileVersionHead(fileId, tx) + const missingProjectProvenance = + owner.entityType === 'project' && currentFile.secretProvenanceVersion === null + const previousProvenance = isVersionHeadCurrent(versionHead, currentFile) + ? undefined + : missingProjectProvenance + ? { status: 'unknown' as const, entries: [] } + : await snapshotWorkspaceFileSecretProvenanceInTx( + tx, + fileId, + currentFile.contentUpdatedAt, + currentFile.secretProvenanceVersion + ) + const sizeDiff = staged.size - getWorkspaceFileSize(currentFile) + const now = new Date() + // A file's content revision must advance even when clocks regress or two writes share a millisecond. + const contentUpdatedAt = new Date( + Math.max(now.getTime(), currentFile.contentUpdatedAt.getTime() + 1) + ) + const [updatedFile] = await tx + .update(workspaceFiles) + .set({ + key: staged.key, + sizeBytes: staged.size, + contentType: staged.contentType, + width: null, + height: null, + updatedAt: now, + contentUpdatedAt, + }) + .where( + and( + eq(workspaceFiles.id, fileId), + fileOwnerCondition(owner), + isNull(workspaceFiles.deletedAt) + ) + ) + .returning() + if (!updatedFile) throw new OrchestrationError('not_found', 'File not found') + let provenancePolicy = options.secretProvenancePolicy + if ( + owner.entityType === 'project' && + ((missingProjectProvenance && provenancePolicy?.mode === 'preserve') || + (provenancePolicy?.mode === 'reinstate' && provenancePolicy.snapshot.status === null)) + ) { + provenancePolicy = { mode: 'replace', provenance: { status: 'unknown' } } + } + const nextProvenance = await applyWorkspaceFileSecretProvenancePolicyInTx( + tx, + fileId, + currentFile, + updatedFile.contentUpdatedAt, + provenancePolicy + ) + const recorded = await recordWorkspaceFileVersionInTx(tx, { + workspaceId: owner.entityType === 'workspace' ? owner.entityId : null, + head: versionHead, + previous: currentFile, + previousProvenance, + next: updatedFile, + nextProvenance, + contentHash: staged.contentHash, + write: options.version, + now, + }) + const storageCleanupEventIds = await enqueueWorkspaceFileStorageCleanups( + tx, + recorded.releasedKeys, + owner.entityType + ) + return { + file: updatedFile, + previous: currentFile, + sizeDiff, + currentVersion: recorded.version, + storageCleanupEventIds, + } +} + /** * Updates a workspace file through a versioned object swap. Blob I/O completes * before the short metadata-and-ledger transaction. @@ -2005,28 +2324,14 @@ export async function updateWorkspaceFileContent( const storageBillingContext = await resolveStorageBillingContext(workspaceId) const nextContentType = contentType || fileRecord.type - const nextStorageKey = generateWorkspaceFileKey(workspaceId, fileRecord.name) - const contentHash = sha256Hex(content) - try { - const metadata: Record = { - originalName: fileRecord.name, - uploadedAt: new Date().toISOString(), - purpose: 'workspace', + const staged = await stageFileContent({ + owner: { entityType: 'workspace', entityId: workspaceId }, userId, - workspaceId, - ...(fileRecord.folderId ? { folderId: fileRecord.folderId } : {}), - } - - const uploadResult = await uploadFile({ - file: content, - fileName: nextStorageKey, + name: fileRecord.name, contentType: nextContentType, - context: 'workspace', - preserveKey: true, - customKey: nextStorageKey, - metadata, - persistMetadata: false, + content, + folderId: fileRecord.folderId, }) let finalized: { @@ -2039,114 +2344,18 @@ export async function updateWorkspaceFileContent( } try { finalized = await db.transaction(async (tx) => { - const [currentFile] = await tx - .select() - .from(workspaceFiles) - .where( - and( - eq(workspaceFiles.id, fileId), - eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), - isNull(workspaceFiles.deletedAt) - ) - ) - .for('update') - .limit(1) - if (!currentFile) { - throw new OrchestrationError('not_found', 'File not found') - } - - // Optimistic-concurrency guard: the row is `FOR UPDATE`-locked, so comparing its committed - // CONTENT version to the caller's expected value and then writing is atomic — a racing writer - // blocks here until this transaction resolves. Compare `contentUpdatedAt` (which advances only on - // content writes), NOT `updatedAt` (which a rename/move also bumps): guarding on `updatedAt` let a - // metadata bump masquerade as an out-of-band CONTENT change, so a racing live-doc persist would - // reconcile stale durable content and clobber in-flight edits. Coalesce to `updatedAt` for rows - // predating the column. A mismatch means the CONTENT changed out-of-band; abort rather than clobber. - if ( - options.expectedUpdatedAt && - currentFile.contentUpdatedAt.getTime() !== options.expectedUpdatedAt.getTime() - ) { - throw new ContentVersionConflictError(fileId) - } - - if (options.collabDocState) { - await saveCollabDocStateInTx(tx, fileId, options.collabDocState) - } - - const versionHead = await loadWorkspaceFileVersionHead(fileId, tx) - const previousProvenance = isVersionHeadCurrent(versionHead, currentFile) - ? undefined - : await snapshotWorkspaceFileSecretProvenanceInTx( - tx, - fileId, - currentFile.contentUpdatedAt, - currentFile.secretProvenanceVersion - ) - - const sizeDiff = content.length - getWorkspaceFileSize(currentFile) - const now = new Date() - // `contentUpdatedAt` is the persist If-Match token, so it MUST be strictly monotonic per file — a - // bare `new Date()` is not: cross-instance clock skew can stamp a later write with an earlier time, - // breaking the version ordering the whole optimistic-concurrency scheme relies on (stuck If-Match, - // wrong reconcile). We hold this row's FOR UPDATE lock, so `currentFile.contentUpdatedAt` is the - // latest committed value; stamp strictly after it. (Also removes same-millisecond collisions.) - // `updatedAt` stays plain wall-clock — it is display/sort only, never the concurrency token. - const contentUpdatedAt = new Date( - Math.max(now.getTime(), currentFile.contentUpdatedAt.getTime() + 1) - ) - const [updatedFile] = await tx - .update(workspaceFiles) - .set({ - key: uploadResult.key, - sizeBytes: content.length, - contentType: nextContentType, - // Replaced bytes: drop the old image's dimensions so the row never describes stale content. - // The next view reserves nothing (the baseline first-load reflow) rather than a wrong-sized - // box, then the browser's measurement backfills the correct value. No server-side decode here - // (avoids EXIF-orientation guesswork), and a late in-flight PATCH that lands after this is - // corrected on the next view since the client overwrites on mismatch. - width: null, - height: null, - updatedAt: now, - contentUpdatedAt, - }) - .where( - and( - eq(workspaceFiles.id, fileId), - eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), - isNull(workspaceFiles.deletedAt) - ) - ) - .returning() - if (!updatedFile) { - throw new OrchestrationError('not_found', 'File not found or could not be updated') - } - - const nextProvenance = await applyWorkspaceFileSecretProvenancePolicyInTx( - tx, + const committed = await commitFileContentInTx(tx, { + owner: { entityType: 'workspace', entityId: workspaceId }, fileId, - currentFile, - updatedFile.contentUpdatedAt, - options.secretProvenancePolicy - ) - const recorded = await recordWorkspaceFileVersionInTx(tx, { - workspaceId, - head: versionHead, - previous: currentFile, - previousProvenance, - next: updatedFile, - nextProvenance, - contentHash, - write: options.version, - now, + staged, + ...options, }) - const storageCleanupEventIds = await enqueueWorkspaceFileStorageCleanups( - tx, - recorded.releasedKeys - ) - + const { + file: updatedFile, + previous: currentFile, + sizeDiff, + storageCleanupEventIds, + } = committed let updatedUsage: number | undefined if (sizeDiff > 0) { updatedUsage = await incrementStorageUsageForBillingContextInTx( @@ -2179,11 +2388,11 @@ export async function updateWorkspaceFileContent( updatedUsage, liveDocEventId, storageCleanupEventIds, - currentVersion: recorded.version, + currentVersion: committed.currentVersion, } }) } catch (finalizationError) { - await cleanupWorkspaceStorageObject(uploadResult.key, 'overwrite finalization failure') + await discardStagedFileContent(staged) throw finalizationError } @@ -2227,6 +2436,7 @@ export async function updateWorkspaceFileContent( logger.info(`Successfully updated workspace file content: ${finalized.file.originalName}`) + if (!finalized.file.userId) throw new Error('Workspace file is missing its creator') return { id: finalized.file.id, workspaceId: finalized.file.workspaceId || workspaceId, @@ -2276,6 +2486,7 @@ export async function deleteWorkspaceFileVersion( version: number ): Promise { const deletion = await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, workspaceId) const [file] = await tx .select({ id: workspaceFiles.id }) .from(workspaceFiles) @@ -2308,57 +2519,75 @@ export async function renameWorkspaceFile( fileId: string, newName: string ): Promise { - logger.info(`Renaming workspace file: ${fileId} to "${newName}" in workspace ${workspaceId}`) - - const trimmedName = newName.trim() - const normalizedName = normalizeWorkspaceFileItemName(trimmedName, 'File') - - const fileRecord = await getWorkspaceFile(workspaceId, fileId, { throwOnError: true }) - if (!fileRecord) { - throw new OrchestrationError('not_found', 'File not found') - } - - if (fileRecord.name === normalizedName) { - return fileRecord - } - - const exists = await fileExistsInWorkspace(workspaceId, normalizedName, fileRecord.folderId) - if (exists) { - throw new FileConflictError(normalizedName) - } + return db.transaction(async (tx) => { + const owner = { entityType: 'workspace', entityId: workspaceId } as const + const row = await renameFileInTx(tx, owner, fileId, newName) + const paths = buildWorkspaceFileFolderPathMap( + await listFileFolders(owner, { scope: 'all' }, tx) + ) + return mapWorkspaceFileRecord(row, workspaceId, paths) + }) +} - let updated: { id: string }[] - const renamedAt = new Date() +/** Renames under the owner's tree and file locks without changing the content identity. */ +async function renameFileInTx( + tx: DbTransaction, + owner: EditableFileOwner, + fileId: string, + newName: string +): Promise { + const name = normalizeWorkspaceFileItemName(newName.trim(), 'File') + if (owner.entityType === 'workspace') await lockWorkspaceProject(tx, owner.entityId) + await acquireFolderMutationLock( + tx, + owner.entityType === 'workspace' ? owner.entityId : `project:${owner.entityId}`, + 'file' + ) + const [file] = await tx + .select() + .from(workspaceFiles) + .where( + and( + fileOwnerCondition(owner), + eq(workspaceFiles.id, fileId), + isNull(workspaceFiles.deletedAt) + ) + ) + .for('update') + .limit(1) + if (!file) throw new OrchestrationError('not_found', 'File not found') + if (file.originalName === name) return file + const [conflict] = await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where( + and( + fileOwnerCondition(owner), + eq(workspaceFiles.originalName, name), + workspaceFileNameFolderCondition(file.folderId), + isNull(workspaceFiles.deletedAt) + ) + ) + .limit(1) + if (conflict) throw new FileConflictError(name) try { - updated = await db + const [renamed] = await tx .update(workspaceFiles) - .set({ originalName: normalizedName, updatedAt: renamedAt }) + .set({ originalName: name, updatedAt: new Date() }) .where( and( + fileOwnerCondition(owner), eq(workspaceFiles.id, fileId), - eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace') + isNull(workspaceFiles.deletedAt) ) ) - .returning({ id: workspaceFiles.id }) - } catch (error: unknown) { - if (getPostgresErrorCode(error) === '23505') { - throw new FileConflictError(normalizedName) - } + .returning() + if (!renamed) throw new OrchestrationError('not_found', 'File not found') + return renamed + } catch (error) { + if (getPostgresErrorCode(error) === '23505') throw new FileConflictError(name) throw error } - - if (updated.length === 0) { - throw new OrchestrationError('not_found', 'File not found or could not be renamed') - } - - logger.info(`Successfully renamed workspace file ${fileId} to "${normalizedName}"`) - - return { - ...fileRecord, - name: normalizedName, - updatedAt: renamedAt, - } } /** @@ -2374,18 +2603,21 @@ export async function deleteWorkspaceFile(workspaceId: string, fileId: string): } if (fileRecord.deletedAt) return - const [archived] = await db - .update(workspaceFiles) - .set({ deletedAt: new Date(), updatedAt: new Date() }) - .where( - and( - eq(workspaceFiles.id, fileId), - eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), - isNull(workspaceFiles.deletedAt) + const [archived] = await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, workspaceId) + return tx + .update(workspaceFiles) + .set({ deletedAt: new Date(), updatedAt: new Date() }) + .where( + and( + eq(workspaceFiles.id, fileId), + eq(workspaceFiles.workspaceId, workspaceId), + eq(workspaceFiles.context, 'workspace'), + isNull(workspaceFiles.deletedAt) + ) ) - ) - .returning() + .returning() + }) if (!archived) return logger.info(`Successfully archived workspace file: ${archived.originalName}`) @@ -2427,6 +2659,7 @@ export async function purgeCreatedWorkspaceFile(params: { isNull(workspaceFiles.deletedAt) ) const cleanupEventIds = await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, params.workspaceId) const [lockedFile] = await tx .select({ id: workspaceFiles.id, @@ -2480,74 +2713,76 @@ export interface PermanentlyDeleteWorkspaceFileResult { } export async function restoreWorkspaceFile(workspaceId: string, fileId: string): Promise { - logger.info(`Restoring workspace file: ${fileId}`) - - const fileRecord = await findWorkspaceFileForLifecycle(db, workspaceId, fileId) - if (!fileRecord) { - throw new OrchestrationError('not_found', 'File not found') - } - - if (!fileRecord.deletedAt) { - return - } - const ws = await getWorkspaceWithOwner(workspaceId) if (!ws || ws.archivedAt) { throw new OrchestrationError('validation', 'Cannot restore file into an archived workspace') } + await db.transaction((tx) => + restoreFileInTx(tx, { entityType: 'workspace', entityId: workspaceId }, fileId) + ) +} - /** - * The file goes back where it was deleted from while that folder is still active. A folder - * archived since would file the row under one the Files page never renders, so re-root it - * instead — the same treatment tables and knowledge bases get on restore. - */ - const restoredFolderId = await resolveRestoredFolderId(fileRecord.folderId, workspaceId, 'file') - - /** - * A concurrent upload/rename can claim the chosen name after `generateRestoreName`'s check (MVCC). - * Retries pick a new random suffix; 23505 maps to {@link FileConflictError} after exhaustion. - */ - const maxUniqueViolationRetries = 8 - let attemptedRestoreName = '' - - for (let attempt = 0; attempt < maxUniqueViolationRetries; attempt++) { - attemptedRestoreName = '' - try { - const newName = await generateRestoreName( - fileRecord.originalName, - (candidate) => fileExistsInWorkspace(workspaceId, candidate, restoredFolderId), - { hasExtension: true } +/** Restores a retained head, re-rooting and deduplicating within its canonical owner. */ +async function restoreFileInTx( + tx: DbTransaction, + owner: EditableFileOwner, + fileId: string +): Promise { + if (owner.entityType === 'workspace') await lockWorkspaceProject(tx, owner.entityId) + await acquireFolderMutationLock( + tx, + owner.entityType === 'workspace' ? owner.entityId : `project:${owner.entityId}`, + 'file' + ) + const [file] = await tx + .select() + .from(workspaceFiles) + .where(and(fileOwnerCondition(owner), eq(workspaceFiles.id, fileId))) + .for('update') + .limit(1) + if (!file) throw new OrchestrationError('not_found', 'File not found') + if (!file.deletedAt) return file + let folderId = file.folderId + if (folderId) { + const [parent] = await tx + .select({ id: folder.id }) + .from(folder) + .where( + and(fileFolderOwnerCondition(owner), eq(folder.id, folderId), isNull(folder.deletedAt)) ) - attemptedRestoreName = newName - - const [restored] = await db - .update(workspaceFiles) - .set({ - deletedAt: null, - folderId: restoredFolderId, - originalName: newName, - updatedAt: new Date(), - }) + .limit(1) + if (!parent) folderId = null + } + const originalName = await generateRestoreName( + file.originalName, + async (name) => { + const [existing] = await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) .where( and( - eq(workspaceFiles.id, fileId), - eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), - isNotNull(workspaceFiles.deletedAt) + fileOwnerCondition(owner), + eq(workspaceFiles.originalName, name), + workspaceFileNameFolderCondition(folderId), + isNull(workspaceFiles.deletedAt) ) ) - .returning() - if (!restored) return - - logger.info(`Successfully restored workspace file: ${newName}`) - return - } catch (error: unknown) { - if (getPostgresErrorCode(error) !== '23505') { - throw error - } - if (attempt === maxUniqueViolationRetries - 1) { - throw new FileConflictError(attemptedRestoreName || fileRecord.originalName) - } - } - } + .limit(1) + return Boolean(existing) + }, + { hasExtension: true } + ) + const [restored] = await tx + .update(workspaceFiles) + .set({ deletedAt: null, folderId, originalName, updatedAt: new Date() }) + .where( + and( + fileOwnerCondition(owner), + eq(workspaceFiles.id, fileId), + isNotNull(workspaceFiles.deletedAt) + ) + ) + .returning() + if (!restored) throw new OrchestrationError('conflict', 'File changed during restoration') + return restored } 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..bf810ba3ae6 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', 'project']), or( isNull(workspaceFiles.secretProvenanceVersion), eq(workspaceFiles.secretProvenanceVersion, 1) @@ -955,7 +955,12 @@ export async function getBoundWorkspaceFileSecretProvenanceByMetadata( continue } if (row.secretProvenanceVersion === null) { - result.set(row.id, EXACT_EMPTY_WORKSPACE_FILE_SECRET_PROVENANCE) + result.set( + row.id, + row.context === 'project' + ? { status: 'unknown' } + : EXACT_EMPTY_WORKSPACE_FILE_SECRET_PROVENANCE + ) continue } if ( @@ -1200,7 +1205,8 @@ export async function filterModelSafeWorkspaceFileAttachments< if ( row.context !== 'workspace' && row.context !== 'mothership' && - row.context !== 'execution' + row.context !== 'execution' && + row.context !== 'project' ) { return true } @@ -1229,7 +1235,7 @@ function classifyModelSafeWorkspaceFileRow( workspaceId?: string ): ModelSafeWorkspaceFileClassification { if (workspaceId && row.workspaceId !== workspaceId) return 'unsafe' - if (row.secretProvenanceVersion === null) return 'safe' + if (row.secretProvenanceVersion === null) return row.context === 'project' ? 'unsafe' : 'safe' if (row.secretProvenanceVersion !== 1) return 'unsafe' const bindingIsCurrent = row.provenanceContentUpdatedAt?.getTime() === row.fileContentUpdatedAt.getTime() @@ -1317,7 +1323,8 @@ export async function areModelSafeWorkspaceFileKeys( if ( row.context !== 'workspace' && row.context !== 'mothership' && - row.context !== 'execution' + row.context !== 'execution' && + row.context !== 'project' ) { continue } diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-accounting.test.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-accounting.test.ts index be5b26628bd..570e7ae5788 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-accounting.test.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-accounting.test.ts @@ -4,6 +4,7 @@ import { dbChainMock, dbChainMockFns, resetDbChainMock } from '@sim/testing' import { billingStorageMock, billingStorageMockFns } from '@sim/testing/mocks/billing-storage.mock' import { folderQueriesMock, folderQueriesMockFns } from '@sim/testing/mocks/folder-queries.mock' import { permissionsMock, permissionsMockFns } from '@sim/testing/mocks/permissions.mock' +import { projectMembershipMock } from '@sim/testing/mocks/project-membership.mock' import { realtimeNotifyMock, realtimeNotifyMockFns } from '@sim/testing/mocks/realtime-notify.mock' import { storageServiceMock, storageServiceMockFns } from '@sim/testing/mocks/storage-service.mock' import { uploadsMock, uploadsMockFns } from '@sim/testing/mocks/uploads.mock' @@ -55,6 +56,8 @@ vi.mock('@/lib/uploads/contexts/workspace/workspace-file-versions', () => ({ vi.mock('@/lib/realtime/notify', () => realtimeNotifyMock) +vi.mock('@/lib/projects/membership', () => projectMembershipMock) + vi.mock('@/lib/uploads/contexts/workspace/workspace-file-live-doc-outbox', () => ({ enqueueWorkspaceFileLiveDocReconciliation: mockEnqueueWorkspaceFileLiveDocReconciliation, processWorkspaceFileLiveDocReconciliationNow: mockProcessWorkspaceFileLiveDocReconciliationNow, @@ -173,6 +176,7 @@ const FILE_ROW = { } describe('workspace file metadata and storage accounting', () => { + let stagedKey = '' beforeEach(() => { resetDbChainMock() mockResolveStorageBillingContext.mockResolvedValue(STORAGE_CONTEXT) @@ -180,11 +184,20 @@ describe('workspace file metadata and storage accounting', () => { mockAssertWorkspaceFileFolderTarget.mockResolvedValue(null) mockHasCloudStorage.mockReturnValue(false) mockHeadObject.mockResolvedValue({ size: FILE_ROW.size }) - mockUploadFile.mockResolvedValue({ key: FILE_ROW.key }) + stagedKey = '' + mockUploadFile.mockReset().mockImplementation(async ({ customKey }) => { + if ( + typeof customKey !== 'string' || + !customKey.startsWith(`workspace/${FILE_ROW.workspaceId}/`) + ) + throw new Error('Upload must preserve its canonical workspace key') + stagedKey = customKey + return { key: customKey } + }) mockGetWorkspaceWithOwner.mockResolvedValue({ archivedAt: null }) mockFileNameExistsInWorkspaceFolder.mockResolvedValue(false) mockResolveRestoredFolderId.mockResolvedValue(null) - mockIncrementStorageUsageForBillingContextInTx.mockResolvedValue(10) + mockIncrementStorageUsageForBillingContextInTx.mockReset().mockResolvedValue(10) mockInitializeWorkspaceFileSecretProvenanceInTx.mockResolvedValue(undefined) mockDecrementStorageUsageForBillingContextInTx.mockResolvedValue(undefined) mockMaybeNotifyStorageLimitForBillingContext.mockResolvedValue(undefined) @@ -201,7 +214,7 @@ describe('workspace file metadata and storage accounting', () => { }) mockProcessWorkspaceFileLiveDocReconciliationNow.mockResolvedValue('completed') mockReplaceWorkspaceFileSecretProvenanceInTx.mockResolvedValue(undefined) - mockSaveCollabDocStateInTx.mockResolvedValue(undefined) + mockSaveCollabDocStateInTx.mockReset().mockResolvedValue(undefined) mockRecordWorkspaceFileVersionInTx.mockResolvedValue({ version: 2, releasedKeys: [] }) }) @@ -261,12 +274,16 @@ describe('workspace file metadata and storage accounting', () => { ) ).rejects.toThrow('payer update failed') - expect(mockDeleteFile).toHaveBeenCalledWith({ key: FILE_ROW.key, context: 'workspace' }) + expect(mockEnqueueWorkspaceFileStorageCleanups).toHaveBeenCalledWith( + expect.any(Object), + [stagedKey], + 'workspace' + ) expect(mockUploadFile.mock.invocationCallOrder[0]).toBeLessThan( dbChainMockFns.transaction.mock.invocationCallOrder[0] ) expect(dbChainMockFns.transaction.mock.invocationCallOrder[0]).toBeLessThan( - mockDeleteFile.mock.invocationCallOrder[0] + mockEnqueueWorkspaceFileStorageCleanups.mock.invocationCallOrder[0] ) }) @@ -452,7 +469,6 @@ describe('workspace file metadata and storage accounting', () => { await deleteWorkspaceFile(FILE_ROW.workspaceId, FILE_ROW.id) expect(mockDecrementStorageUsageForBillingContextInTx).not.toHaveBeenCalled() - expect(dbChainMockFns.transaction).not.toHaveBeenCalled() }) it('re-roots a restored file whose folder has since been archived', async () => { @@ -467,11 +483,6 @@ describe('workspace file metadata and storage accounting', () => { await restoreWorkspaceFile(FILE_ROW.workspaceId, FILE_ROW.id) - expect(mockFileNameExistsInWorkspaceFolder).toHaveBeenCalledWith( - FILE_ROW.workspaceId, - FILE_ROW.originalName, - null - ) expect(dbChainMockFns.set).toHaveBeenCalledWith( expect.objectContaining({ deletedAt: null, folderId: null }) ) @@ -489,7 +500,6 @@ describe('workspace file metadata and storage accounting', () => { } dbChainMockFns.limit.mockResolvedValueOnce([FILE_ROW]).mockResolvedValueOnce([concurrentFile]) dbChainMockFns.returning.mockResolvedValueOnce([updatedFile]) - mockUploadFile.mockResolvedValueOnce({ key: replacementKey }) const updated = await updateWorkspaceFileContent( FILE_ROW.workspaceId, @@ -529,7 +539,11 @@ describe('workspace file metadata and storage accounting', () => { }) ) expect(mockDeleteFile).not.toHaveBeenCalled() - expect(mockEnqueueWorkspaceFileStorageCleanups).toHaveBeenCalledWith(expect.any(Object), []) + expect(mockEnqueueWorkspaceFileStorageCleanups).toHaveBeenCalledWith( + expect.any(Object), + [], + 'workspace' + ) expect(updated.key).toBe(replacementKey) expect(mockUploadFile.mock.invocationCallOrder[0]).toBeLessThan( dbChainMockFns.transaction.mock.invocationCallOrder[0] @@ -543,9 +557,11 @@ describe('workspace file metadata and storage accounting', () => { let committed = false dbChainMockFns.transaction.mockImplementationOnce(async (callback) => { const result = await callback(transaction) - expect(mockEnqueueWorkspaceFileStorageCleanups).toHaveBeenCalledWith(transaction, [ - FILE_ROW.key, - ]) + expect(mockEnqueueWorkspaceFileStorageCleanups).toHaveBeenCalledWith( + transaction, + [FILE_ROW.key], + 'workspace' + ) expect(mockProcessWorkspaceFileStorageCleanupsNow).not.toHaveBeenCalled() committed = true return result @@ -555,7 +571,7 @@ describe('workspace file metadata and storage accounting', () => { }) dbChainMockFns.limit.mockResolvedValueOnce([FILE_ROW]).mockResolvedValueOnce([FILE_ROW]) dbChainMockFns.returning.mockResolvedValueOnce([updatedFile]) - mockUploadFile.mockResolvedValueOnce({ key: replacementKey }) + mockRecordWorkspaceFileVersionInTx.mockResolvedValueOnce({ version: 2, releasedKeys: [FILE_ROW.key], @@ -582,7 +598,7 @@ describe('workspace file metadata and storage accounting', () => { const updatedFile = { ...FILE_ROW, key: replacementKey, size: 10, sizeBytes: 10 } dbChainMockFns.limit.mockResolvedValueOnce([FILE_ROW]).mockResolvedValueOnce([FILE_ROW]) dbChainMockFns.returning.mockResolvedValueOnce([updatedFile]) - mockUploadFile.mockResolvedValueOnce({ key: replacementKey }) + mockIncrementStorageUsageForBillingContextInTx.mockRejectedValueOnce( new Error('Storage limit exceeded') ) @@ -598,8 +614,12 @@ describe('workspace file metadata and storage accounting', () => { ) ).rejects.toThrow('Storage limit exceeded') - expect(mockDeleteFile).toHaveBeenCalledTimes(1) - expect(mockDeleteFile).toHaveBeenCalledWith({ key: replacementKey, context: 'workspace' }) + expect(mockEnqueueWorkspaceFileStorageCleanups).toHaveBeenCalledWith( + expect.any(Object), + [stagedKey], + 'workspace' + ) + expect(stagedKey).not.toBe(FILE_ROW.key) }) const MD_ROW = { ...FILE_ROW, originalName: 'note.md', contentType: 'text/markdown' } @@ -638,7 +658,6 @@ describe('workspace file metadata and storage accounting', () => { }) dbChainMockFns.limit.mockResolvedValueOnce([MD_ROW]).mockResolvedValueOnce([MD_ROW]) dbChainMockFns.returning.mockResolvedValueOnce([updatedFile]) - mockUploadFile.mockResolvedValueOnce({ key: replacementKey }) await expect( updateWorkspaceFileContent(MD_ROW.workspaceId, MD_ROW.id, MD_ROW.userId, content, undefined, { @@ -663,16 +682,10 @@ describe('workspace file metadata and storage accounting', () => { expect(mockDeleteFile).not.toHaveBeenCalled() }) - it.each([ - { expectedState: PREPARED_COLLAB_STATE.expectedState, cleanupFails: false }, - { expectedState: PREPARED_COLLAB_STATE.expectedState, cleanupFails: true }, - { expectedState: null, cleanupFails: false }, - { expectedState: null, cleanupFails: true }, - ])( + it.each([{ expectedState: PREPARED_COLLAB_STATE.expectedState }, { expectedState: null }])( 'aborts finalization on a collab-state conflict and cleans only the staged blob: %j', - async ({ expectedState, cleanupFails }) => { + async ({ expectedState }) => { const transaction = { ...dbChainMock.db } - const replacementKey = `${MD_ROW.key}-replacement` const conflict = new CollabDocStateConflictError(MD_ROW.id) const preparedState = { ...PREPARED_COLLAB_STATE, expectedState } let rolledBack = false @@ -685,11 +698,10 @@ describe('workspace file metadata and storage accounting', () => { } }) dbChainMockFns.limit.mockResolvedValueOnce([MD_ROW]).mockResolvedValueOnce([MD_ROW]) - mockUploadFile.mockResolvedValueOnce({ key: replacementKey }) + mockSaveCollabDocStateInTx.mockRejectedValueOnce(conflict) - mockDeleteFile.mockImplementationOnce(async () => { + mockProcessWorkspaceFileStorageCleanupsNow.mockImplementationOnce(async () => { expect(rolledBack).toBe(true) - if (cleanupFails) throw new Error('storage unavailable') }) await expect( @@ -715,17 +727,16 @@ describe('workspace file metadata and storage accounting', () => { expect(mockEnqueueWorkspaceFileLiveDocReconciliation).not.toHaveBeenCalled() expect(mockProcessWorkspaceFileLiveDocReconciliationNow).not.toHaveBeenCalled() expect(mockMaybeNotifyStorageLimitForBillingContext).not.toHaveBeenCalled() - expect(mockDeleteFile).toHaveBeenCalledExactlyOnceWith({ - key: replacementKey, - context: 'workspace', - }) + expect(mockEnqueueWorkspaceFileStorageCleanups).toHaveBeenCalledWith( + expect.any(Object), + [stagedKey], + 'workspace' + ) } ) it('does not accept the prepared collab state before validating the locked content version', async () => { - const replacementKey = `${MD_ROW.key}-replacement` dbChainMockFns.limit.mockResolvedValueOnce([MD_ROW]).mockResolvedValueOnce([MD_ROW]) - mockUploadFile.mockResolvedValueOnce({ key: replacementKey }) await expect( updateWorkspaceFileContent( @@ -744,10 +755,11 @@ describe('workspace file metadata and storage accounting', () => { expect(mockSaveCollabDocStateInTx).not.toHaveBeenCalled() expect(dbChainMockFns.update).not.toHaveBeenCalled() - expect(mockDeleteFile).toHaveBeenCalledExactlyOnceWith({ - key: replacementKey, - context: 'workspace', - }) + expect(mockEnqueueWorkspaceFileStorageCleanups).toHaveBeenCalledWith( + expect.any(Object), + [stagedKey], + 'workspace' + ) }) it('rejects the shared transaction when accounting fails after accepting the collab state', async () => { @@ -763,7 +775,7 @@ describe('workspace file metadata and storage accounting', () => { dbChainMockFns.returning.mockResolvedValueOnce([ { ...MD_ROW, key: replacementKey, sizeBytes: 13 }, ]) - mockUploadFile.mockResolvedValueOnce({ key: replacementKey }) + mockIncrementStorageUsageForBillingContextInTx.mockRejectedValueOnce( new Error('accounting unavailable') ) @@ -796,10 +808,11 @@ describe('workspace file metadata and storage accounting', () => { ) expect(mockEnqueueWorkspaceFileLiveDocReconciliation).not.toHaveBeenCalled() expect(mockMaybeNotifyStorageLimitForBillingContext).not.toHaveBeenCalled() - expect(mockDeleteFile).toHaveBeenCalledExactlyOnceWith({ - key: replacementKey, - context: 'workspace', - }) + expect(mockEnqueueWorkspaceFileStorageCleanups).toHaveBeenCalledWith( + expect.any(Object), + [stagedKey], + 'workspace' + ) }) it('transactionally enqueues a markdown overwrite for live-document reconciliation', async () => { @@ -830,7 +843,6 @@ describe('workspace file metadata and storage accounting', () => { } dbChainMockFns.limit.mockResolvedValueOnce([MD_ROW]).mockResolvedValueOnce([MD_ROW]) dbChainMockFns.returning.mockResolvedValueOnce([updatedFile]) - mockUploadFile.mockResolvedValueOnce({ key: MD_ROW.key }) await updateWorkspaceFileContent( MD_ROW.workspaceId, @@ -864,7 +876,6 @@ describe('workspace file metadata and storage accounting', () => { .mockResolvedValueOnce([markdownByType]) .mockResolvedValueOnce([markdownByType]) dbChainMockFns.returning.mockResolvedValueOnce([updatedFile]) - mockUploadFile.mockResolvedValueOnce({ key: markdownByType.key }) await updateWorkspaceFileContent( markdownByType.workspaceId, @@ -885,7 +896,6 @@ describe('workspace file metadata and storage accounting', () => { it('throws ContentVersionConflictError and does not write when the guard mismatches', async () => { // The locked row's updatedAt differs from the caller's expected value → out-of-band edit. dbChainMockFns.limit.mockResolvedValueOnce([FILE_ROW]).mockResolvedValueOnce([FILE_ROW]) - mockUploadFile.mockResolvedValueOnce({ key: FILE_ROW.key }) await expect( updateWorkspaceFileContent( @@ -900,6 +910,10 @@ describe('workspace file metadata and storage accounting', () => { // Never advanced the row, and cleaned up the orphan upload it staged before the conflict. expect(dbChainMockFns.returning).not.toHaveBeenCalled() - expect(mockDeleteFile).toHaveBeenCalledWith({ key: FILE_ROW.key, context: 'workspace' }) + expect(mockEnqueueWorkspaceFileStorageCleanups).toHaveBeenCalledWith( + expect.any(Object), + [stagedKey], + 'workspace' + ) }) }) diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox.ts index e97dd72278e..b8b5daa7d73 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox.ts @@ -18,6 +18,7 @@ export const WORKSPACE_FILE_STORAGE_CLEANUP_OUTBOX_EVENT = 'workspace-file.stora interface WorkspaceFileStorageCleanupPayload { key: string + context?: 'workspace' | 'project' } function parsePayload(payload: unknown): WorkspaceFileStorageCleanupPayload { @@ -28,14 +29,21 @@ function parsePayload(payload: unknown): WorkspaceFileStorageCleanupPayload { if (typeof key !== 'string' || key.trim().length === 0) { throw new Error('Workspace file storage cleanup outbox payload is missing key') } - return { key } + const context = payload.context + if (context !== undefined && context !== 'workspace' && context !== 'project') { + throw new Error('File storage cleanup has an invalid storage context') + } + if (key.startsWith('project/') && context !== 'project') { + throw new Error('Project object cleanup requires its explicit storage context') + } + return { key, context } } const cleanupWorkspaceFileStorage: OutboxHandler = async (rawPayload, context) => { const payload = parsePayload(rawPayload) context.signal.throwIfAborted() try { - await deleteFile({ key: payload.key, context: 'workspace' }) + await deleteFile({ key: payload.key, context: payload.context ?? 'workspace' }) } catch (error) { if (describeError(error).code === 'ENOENT') return throw error @@ -52,7 +60,9 @@ export const workspaceFileStorageCleanupOutboxHandlers = { */ export async function enqueueWorkspaceFileStorageCleanups( executor: Pick, - keys: readonly string[] + keys: readonly string[], + context?: 'workspace' | 'project', + options: { availableAt?: Date } = {} ): Promise { const eventIds: string[] = [] for (const chunk of chunkArray([...keys], MAX_BULK_ENQUEUE_EVENTS)) { @@ -60,7 +70,10 @@ export async function enqueueWorkspaceFileStorageCleanups( ...(await enqueueOutboxEvents( executor, WORKSPACE_FILE_STORAGE_CLEANUP_OUTBOX_EVENT, - chunk.map((key): WorkspaceFileStorageCleanupPayload => ({ key })) + chunk.map( + (key): WorkspaceFileStorageCleanupPayload => ({ key, ...(context ? { context } : {}) }) + ), + options )) ) } diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts index 6e8b0dbc743..59821ccd553 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts @@ -23,6 +23,7 @@ import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace/works import type { WorkspaceFileSecretProvenanceSnapshot } from '@/lib/uploads/contexts/workspace/workspace-file-secret-provenance' import { enqueueWorkspaceFileStorageCleanups } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' import { getWorkspaceFileSize } from '@/lib/uploads/shared/types' +import { resolveFileOwner } from '@/lib/workspace-files/ownership' /** A coalescing version stops absorbing writes this long after it was opened. */ const FILE_VERSION_COALESCE_WINDOW_MS = 10 * 60 * 1000 @@ -164,7 +165,7 @@ async function supersedeVersionInTx(tx: DbTransaction, versionId: string, now: D interface RecordWorkspaceFileVersionParams { /** The workspace the write was scoped to; the file row's column is nullable for other contexts. */ - workspaceId: string + workspaceId: string | null /** Head row loaded before the file row was updated. */ head: WorkspaceFileVersionSummaryRow | undefined /** The file row as it was before this write (locked). */ @@ -226,7 +227,12 @@ export async function recordWorkspaceFileVersionInTx( ...contentColumns(previous, params.previousProvenance), contentHash: null, source: original ? 'upload' : 'unknown', - authorUserIds: original ? [previous.userId] : [], + authorUserIds: + original && (previous.originalCreatorUserId ?? previous.userId) + ? [previous.originalCreatorUserId ?? previous.userId].filter( + (id): id is string => id !== null + ) + : [], createdAt: previous.contentUpdatedAt, updatedAt: previous.contentUpdatedAt, }) @@ -339,7 +345,7 @@ export async function releaseWorkspaceFileVersionsForPurgeInTx( ): Promise { if (fileIds.length === 0) return const expired = await tx - .select({ id: workspaceFiles.id, key: workspaceFiles.key }) + .select() .from(workspaceFiles) .where( and( @@ -348,8 +354,16 @@ export async function releaseWorkspaceFileVersionsForPurgeInTx( lt(workspaceFiles.deletedAt, deletedBefore) ) ) + .orderBy(workspaceFiles.id) .for('update') if (expired.length === 0) return + const contexts = new Map() + for (const file of expired) { + const owner = resolveFileOwner(file) + if (!owner || (owner.entityType !== 'workspace' && owner.entityType !== 'project')) + throw new Error('File version purge requires canonical editable ownership') + contexts.set(file.id, owner.entityType) + } const released = await tx .delete(workspaceFileVersion) .where( @@ -364,11 +378,14 @@ export async function releaseWorkspaceFileVersionsForPurgeInTx( ) ) ) - .returning({ key: workspaceFileVersion.key }) - await enqueueWorkspaceFileStorageCleanups( - tx, - released.map((row) => row.key) - ) + .returning({ fileId: workspaceFileVersion.fileId, key: workspaceFileVersion.key }) + for (const context of ['workspace', 'project'] as const) { + await enqueueWorkspaceFileStorageCleanups( + tx, + released.filter((row) => contexts.get(row.fileId) === context).map((row) => row.key), + context + ) + } } /** Deletes superseded versions beyond {@link MAX_SUPERSEDED_FILE_VERSIONS}, returning their keys. */ @@ -400,8 +417,8 @@ async function pruneExcessWorkspaceFileVersionsInTx( /** The file fields that identify its current bytes. */ type WorkspaceFileVersionSubject = Pick< WorkspaceFileRecord, - 'id' | 'key' | 'size' | 'type' | 'uploadedBy' | 'uploadedAt' | 'updatedAt' | 'contentUpdatedAt' -> + 'id' | 'key' | 'size' | 'type' | 'uploadedAt' | 'updatedAt' | 'contentUpdatedAt' +> & { uploadedBy: string | null; originalCreatorUserId?: string | null } /** One version of a workspace file as readers see it. */ export interface WorkspaceFileVersionRecord { @@ -472,6 +489,7 @@ function implicitCurrentVersion( const contentUpdatedAt = contentVersionTime(file) const original = !head && isOriginalUploadContent({ uploadedAt: file.uploadedAt, contentUpdatedAt }) + const originalAuthor = file.originalCreatorUserId ?? file.uploadedBy return { fileId: file.id, version: (head?.version ?? 0) + 1, @@ -479,7 +497,7 @@ function implicitCurrentVersion( size: file.size, contentType: file.type, source: original ? 'upload' : 'unknown', - authorUserIds: original ? [file.uploadedBy] : [], + authorUserIds: original && originalAuthor ? [originalAuthor] : [], restoredFromVersion: null, isCurrent: true, createdAt: contentUpdatedAt, @@ -495,39 +513,42 @@ function implicitCurrentVersion( */ function withVersionSnapshot( file: WorkspaceFileVersionSubject, - read: (tx: DbTransaction, file: WorkspaceFileVersionSubject) => Promise + read: (tx: DbTransaction, file: WorkspaceFileVersionSubject) => Promise, + executor?: DbTransaction ): Promise { - return db.transaction( - async (tx) => { - const [row] = await tx - .select({ - key: workspaceFiles.key, - sizeBytes: workspaceFiles.sizeBytes, - contentType: workspaceFiles.contentType, - userId: workspaceFiles.userId, - uploadedAt: workspaceFiles.uploadedAt, - updatedAt: workspaceFiles.updatedAt, - contentUpdatedAt: workspaceFiles.contentUpdatedAt, - }) - .from(workspaceFiles) - .where(eq(workspaceFiles.id, file.id)) - .limit(1) - const snapshot: WorkspaceFileVersionSubject = row - ? { - id: file.id, - key: row.key, - size: getWorkspaceFileSize(row), - type: row.contentType, - uploadedBy: row.userId, - uploadedAt: row.uploadedAt, - updatedAt: row.updatedAt, - contentUpdatedAt: row.contentUpdatedAt, - } - : file - return read(tx, snapshot) - }, - { isolationLevel: 'repeatable read', accessMode: 'read only' } - ) + const readSnapshot = async (tx: DbTransaction) => { + const [row] = await tx + .select({ + key: workspaceFiles.key, + sizeBytes: workspaceFiles.sizeBytes, + contentType: workspaceFiles.contentType, + userId: workspaceFiles.userId, + originalCreatorUserId: workspaceFiles.originalCreatorUserId, + uploadedAt: workspaceFiles.uploadedAt, + updatedAt: workspaceFiles.updatedAt, + contentUpdatedAt: workspaceFiles.contentUpdatedAt, + }) + .from(workspaceFiles) + .where(eq(workspaceFiles.id, file.id)) + .limit(1) + const snapshot: WorkspaceFileVersionSubject = row + ? { + id: file.id, + key: row.key, + size: getWorkspaceFileSize(row), + type: row.contentType, + uploadedBy: row.userId, + originalCreatorUserId: row.originalCreatorUserId, + uploadedAt: row.uploadedAt, + updatedAt: row.updatedAt, + contentUpdatedAt: row.contentUpdatedAt, + } + : file + return read(tx, snapshot) + } + return executor + ? readSnapshot(executor) + : db.transaction(readSnapshot, { isolationLevel: 'repeatable read', accessMode: 'read only' }) } /** The version holding the file's current bytes, recorded or implicit, within a snapshot. */ @@ -548,40 +569,45 @@ const VERSION_KEYSET: readonly KeysetKey<{ version: number }>[] = [ /** A page of a file's versions ordered by version number, resumable by keyset. */ export async function queryWorkspaceFileVersions( file: WorkspaceFileVersionSubject, - options: { sortOrder: ListSortOrder; limit: number; after?: CursorKey[] } + options: { sortOrder: ListSortOrder; limit: number; after?: CursorKey[] }, + executor?: DbTransaction ): Promise<{ versions: WorkspaceFileVersionRecord[]; nextKeys: CursorKey[] | null }> { const resume = resumeKeyset(VERSION_KEYSET, options.after, options.sortOrder) - return withVersionSnapshot(file, async (tx, current) => { - const rows = await tx - .select(versionSummaryColumns) - .from(workspaceFileVersion) - .where(and(eq(workspaceFileVersion.fileId, current.id), resume)) - .orderBy(...listOrderBy(keysetColumns(VERSION_KEYSET), options.sortOrder)) - .limit(options.limit + 1) - const head = await loadWorkspaceFileVersionHead(current.id, tx) - const records = rows.map((row) => toVersionRecord(row, current)) - /** - * The implicit current version numbers above every row, so it leads a descending list and ends - * an ascending one; a cursor already past it leaves it out. Over-fetching by one row still - * decides whether another page follows, since the cut keeps the first `limit` records either way. - */ - const implicit = isVersionHeadCurrent(head, current) - ? null - : implicitCurrentVersion(current, head) - const resumeAfter = options.after?.[0] - if ( - implicit && - (typeof resumeAfter !== 'number' || - (options.sortOrder === 'desc' - ? implicit.version < resumeAfter - : implicit.version > resumeAfter)) - ) { - if (options.sortOrder === 'desc') records.unshift(implicit) - else records.push(implicit) - } - const page = keysetPage(VERSION_KEYSET, records, options.limit) - return { versions: page.data, nextKeys: page.nextCursorKeys } - }) + return withVersionSnapshot( + file, + async (tx, current) => { + const rows = await tx + .select(versionSummaryColumns) + .from(workspaceFileVersion) + .where(and(eq(workspaceFileVersion.fileId, current.id), resume)) + .orderBy(...listOrderBy(keysetColumns(VERSION_KEYSET), options.sortOrder)) + .limit(options.limit + 1) + const head = await loadWorkspaceFileVersionHead(current.id, tx) + const records = rows.map((row) => toVersionRecord(row, current)) + /** + * The implicit current version numbers above every row, so it leads a descending list and ends + * an ascending one; a cursor already past it leaves it out. Over-fetching by one row still + * decides whether another page follows, since the cut keeps the first `limit` records either way. + */ + const implicit = isVersionHeadCurrent(head, current) + ? null + : implicitCurrentVersion(current, head) + const resumeAfter = options.after?.[0] + if ( + implicit && + (typeof resumeAfter !== 'number' || + (options.sortOrder === 'desc' + ? implicit.version < resumeAfter + : implicit.version > resumeAfter)) + ) { + if (options.sortOrder === 'desc') records.unshift(implicit) + else records.push(implicit) + } + const page = keysetPage(VERSION_KEYSET, records, options.limit) + return { versions: page.data, nextKeys: page.nextCursorKeys } + }, + executor + ) } /** @@ -601,9 +627,10 @@ export async function findWorkspaceFileVersionKeys(keys: readonly string[]): Pro /** The version holding the file's current bytes, recorded or implicit. */ export function getCurrentWorkspaceFileVersion( - file: WorkspaceFileVersionSubject + file: WorkspaceFileVersionSubject, + executor?: DbTransaction ): Promise { - return withVersionSnapshot(file, currentVersionInSnapshot) + return withVersionSnapshot(file, currentVersionInSnapshot, executor) } /** @@ -639,20 +666,28 @@ export function currentWorkspaceFileVersionNumberSql() { /** One version of a file, or null when it never existed or retention removed it. */ export function getWorkspaceFileVersion( file: WorkspaceFileVersionSubject, - version: number + version: number, + executor?: DbTransaction ): Promise { - return withVersionSnapshot(file, async (tx, current) => { - const [row] = await tx - .select(versionSummaryColumns) - .from(workspaceFileVersion) - .where( - and(eq(workspaceFileVersion.fileId, current.id), eq(workspaceFileVersion.version, version)) - ) - .limit(1) - if (row) return toVersionRecord(row, current) - const latest = await currentVersionInSnapshot(tx, current) - return latest.version === version ? latest : null - }) + return withVersionSnapshot( + file, + async (tx, current) => { + const [row] = await tx + .select(versionSummaryColumns) + .from(workspaceFileVersion) + .where( + and( + eq(workspaceFileVersion.fileId, current.id), + eq(workspaceFileVersion.version, version) + ) + ) + .limit(1) + if (row) return toVersionRecord(row, current) + const latest = await currentVersionInSnapshot(tx, current) + return latest.version === version ? latest : null + }, + executor + ) } /** @@ -663,9 +698,10 @@ export function getWorkspaceFileVersion( export async function getWorkspaceFileVersionProvenance( fileId: string, version: number, - expectedKey?: string + expectedKey?: string, + executor: DbOrTx = db ): Promise { - const [row] = await db + const [row] = await executor .select({ status: workspaceFileVersion.secretProvenanceStatus, entries: workspaceFileVersion.secretProvenanceEntries, diff --git a/apps/sim/lib/uploads/core/storage-client.ts b/apps/sim/lib/uploads/core/storage-client.ts index 8d00a1559ef..378ac3fb749 100644 --- a/apps/sim/lib/uploads/core/storage-client.ts +++ b/apps/sim/lib/uploads/core/storage-client.ts @@ -25,7 +25,11 @@ export async function getFileMetadata( if (metadataRecord) { return { - userId: metadataRecord.userId, + ...(metadataRecord.entityType !== 'project' && + metadataRecord.context !== 'project' && + metadataRecord.userId + ? { userId: metadataRecord.userId } + : {}), workspaceId: metadataRecord.workspaceId || '', originalName: metadataRecord.originalName, uploadedAt: metadataRecord.uploadedAt.toISOString(), diff --git a/apps/sim/lib/uploads/documents/compile-error.ts b/apps/sim/lib/uploads/documents/compile-error.ts new file mode 100644 index 00000000000..587fdd9a698 --- /dev/null +++ b/apps/sim/lib/uploads/documents/compile-error.ts @@ -0,0 +1,11 @@ +/** Classifies a failed document build or a compiled artifact that is still pending. */ +export class DocCompileUserError extends Error { + /** True when the artifact is still compiling and a retry may succeed. */ + readonly pending: boolean + + constructor(message: string, options?: { pending?: boolean }) { + super(message) + this.name = 'DocCompileUserError' + this.pending = options?.pending ?? false + } +} diff --git a/apps/sim/lib/mothership/tools/server/files/doc-compile.test.ts b/apps/sim/lib/uploads/documents/compile.test.ts similarity index 98% rename from apps/sim/lib/mothership/tools/server/files/doc-compile.test.ts rename to apps/sim/lib/uploads/documents/compile.test.ts index 642253772e9..1870928cad9 100644 --- a/apps/sim/lib/mothership/tools/server/files/doc-compile.test.ts +++ b/apps/sim/lib/uploads/documents/compile.test.ts @@ -26,14 +26,14 @@ vi.mock('@/lib/workspace-files/application/read-workspace-file-content', () => ( vi.mock('@/lib/workspace-files/application/read-workspace-file-metadata', () => ({ readWorkspaceFileMetadata: { execute: readWorkspaceFileMetadataMock }, })) -vi.mock('./doc-compiled-store', () => ({ +vi.mock('@/lib/uploads/documents/compiled-store', () => ({ loadCompiledDoc: loadCompiledDocMock, loadPublishedCompiledDoc: vi.fn(), publishCompiledDocArtifact: publishCompiledDocArtifactMock, storeCompiledDoc: storeCompiledDocMock, })) -import { collectReferencedFileIds, compileDoc } from './doc-compile' +import { collectReferencedFileIds, compileDoc } from '@/lib/uploads/documents/compile' const executeInSandboxMock = remoteSandboxMockFns.mockExecuteInSandbox diff --git a/apps/sim/lib/mothership/tools/server/files/doc-compile.ts b/apps/sim/lib/uploads/documents/compile.ts similarity index 92% rename from apps/sim/lib/mothership/tools/server/files/doc-compile.ts rename to apps/sim/lib/uploads/documents/compile.ts index 030128fe095..80ea9bcde68 100644 --- a/apps/sim/lib/mothership/tools/server/files/doc-compile.ts +++ b/apps/sim/lib/uploads/documents/compile.ts @@ -1,6 +1,7 @@ import type { Principal } from '@sim/auth/principal' import { createLogger } from '@sim/logger' import { sha256Hex } from '@sim/security/hash' +import { compareStrings } from '@sim/utils/string' import { isDocSandboxEnabled } from '@/lib/core/config/env-flags' import { CodeLanguage } from '@/lib/execution/languages' import { @@ -9,19 +10,21 @@ import { type SandboxFile, } from '@/lib/execution/remote-sandbox' import { runSandboxTask } from '@/lib/execution/sandbox/run-task' -import { DocCompileUserError } from '@/lib/mothership/tools/server/files/doc-compile-error' +import type { WorkspaceFileSecretProvenanceIdentity } from '@/lib/uploads/contexts/workspace/workspace-file-secret-provenance' +import { DocCompileUserError } from '@/lib/uploads/documents/compile-error' import { type CompiledDocReadOptions, + compiledArtifactKey, loadCompiledDoc, loadPublishedCompiledDoc, publishCompiledDocArtifact, storeCompiledDoc, -} from '@/lib/mothership/tools/server/files/doc-compiled-store' -import { PPTX_SHIM_JS } from '@/lib/mothership/tools/server/files/pptx-shim' -import type { WorkspaceFileSecretProvenanceIdentity } from '@/lib/uploads/contexts/workspace/workspace-file-secret-provenance' +} from '@/lib/uploads/documents/compiled-store' +import { PPTX_SHIM_JS } from '@/lib/uploads/documents/pptx-shim' +import { iterateDocumentFileReferences } from '@/lib/uploads/documents/references' +import { getFileExtension, getMimeTypeFromExtension } from '@/lib/uploads/utils/file-utils' import { readWorkspaceFileContent } from '@/lib/workspace-files/application/read-workspace-file-content' import { readWorkspaceFileMetadata } from '@/lib/workspace-files/application/read-workspace-file-metadata' -import { getContentType } from '@/app/api/files/utils' import type { SandboxTaskId } from '@/sandbox-tasks/registry' const logger = createLogger('CopilotDocCompile') @@ -37,12 +40,12 @@ const PDF_MIME = 'application/pdf' // JS path; the python engines have distinct markers. export const PPTXGENJS_SOURCE_MIME = 'text/x-pptxgenjs' export const DOCXJS_SOURCE_MIME = 'text/x-docxjs' -export const PYTHON_PDF_SOURCE_MIME = 'text/x-python-pdf' -export const PYTHON_XLSX_SOURCE_MIME = 'text/x-python-xlsx' +const PYTHON_PDF_SOURCE_MIME = 'text/x-python-pdf' +const PYTHON_XLSX_SOURCE_MIME = 'text/x-python-xlsx' -export type DocEngine = 'node' | 'python' +type DocEngine = 'node' | 'python' -export interface E2BDocFormat { +interface E2BDocFormat { ext: 'pptx' | 'docx' | 'pdf' | 'xlsx' engine: DocEngine formatName: 'PPTX' | 'DOCX' | 'PDF' | 'XLSX' @@ -96,17 +99,6 @@ export async function getE2BDocFormat(fileName: string): Promise` path, so referenced files are staged -// before the script runs. Without this the sandbox `getFileBase64` throws -// "file not staged" and every workspace-image embed silently fails. -const INPUT_PATH_RE = /\/home\/user\/inputs\/([A-Za-z0-9_-]+)/g -const FILE_HELPER_RE = - /\b(?:getFileBase64|addImage|drawImage|input_path)\(\s*(?:[A-Za-z_$][\w$]*\s*,\s*)?['"]([A-Za-z0-9_-]+)['"]/g - /** * A .pptx or .docx source whose first line is `#!simdoc` is a template-clone * script: Python against the simdoc Deck/Doc API (opening a retained reference @@ -116,7 +108,7 @@ const FILE_HELPER_RE = */ const SIMDOC_DECK_MARKER = '#!simdoc' -export function isSimdocDeckSource(source: string): boolean { +function isSimdocDeckSource(source: string): boolean { return source.trimStart().startsWith(SIMDOC_DECK_MARKER) } @@ -143,7 +135,8 @@ interface ReferencedImageResolution { artifactIdentity?: string } -export interface CompiledDocResult { +interface CompiledDocResult { + artifactKey?: string buffer: Buffer contentType: string contributingFiles?: readonly WorkspaceFileSecretProvenanceIdentity[] @@ -193,13 +186,9 @@ function referencedImageIdentities( */ export function collectReferencedFileIds(source: string): Set { const ids = new Set() - for (const re of [INPUT_PATH_RE, FILE_HELPER_RE]) { - for (const match of source.matchAll(re)) { - if (match[1]) { - ids.add(match[1]) - if (ids.size > MAX_REFERENCED_INPUTS) return ids - } - } + for (const { fileId } of iterateDocumentFileReferences(source)) { + ids.add(fileId) + if (ids.size > MAX_REFERENCED_INPUTS) return ids } return ids } @@ -223,7 +212,7 @@ async function resolveReferencedImages( const images: ResolvedReferencedImage[] = [] const identity: Array> = [] - for (const fileId of ids) { + for (const fileId of [...ids].sort(compareStrings)) { const { file: record } = await readWorkspaceFileMetadata.execute({ principal, input: { fileId, assertedWorkspaceId: workspaceId }, @@ -357,11 +346,10 @@ interface CompileArgs extends LegacyCompileArgs { * Internal — callers use compileDoc (load-or-build + store). */ async function compileDocViaE2BPython( - { source, workspaceId, filePrincipal }: CompileArgs, + source: string, fmt: E2BDocFormat, - referencedImages: ReferencedImageResolution + sandboxFiles: SandboxFile[] ): Promise { - const sandboxFiles = await stageReferencedImages(referencedImages, workspaceId, filePrincipal) const outputSandboxPath = `/home/user/output.${fmt.ext}` // openpyxl writes formula strings but no cached values, so a web viewer (SheetJS) @@ -392,7 +380,6 @@ async function compileDocViaE2BPython( return Buffer.from(result.exportedFileContent, 'base64') } -// ── Node engine (pptxgenjs / docx) ────────────────────────────────────────── // Preambles replicate the isolated-vm bootstraps as node globals: the injected // `pptx`/`docx` instances, geometry constants, and fileId-based image helpers // (reading staged /home/user/inputs/ files). pptx also gets `iconImage` @@ -440,11 +427,10 @@ fs.writeFileSync('/home/user/output.docx', __buf); * engines. Throws DocCompileUserError on a script error. */ async function compileDocViaE2BNode( - { source, workspaceId, filePrincipal }: CompileArgs, + source: string, ext: 'pptx' | 'docx', - referencedImages: ReferencedImageResolution + sandboxFiles: SandboxFile[] ): Promise { - const sandboxFiles = await stageReferencedImages(referencedImages, workspaceId, filePrincipal) const outputSandboxPath = `/home/user/output.${ext}` const preamble = ext === 'pptx' ? PPTX_NODE_PREAMBLE : DOCX_NODE_PREAMBLE const finalize = ext === 'pptx' ? PPTX_NODE_FINALIZE : DOCX_NODE_FINALIZE @@ -554,30 +540,9 @@ async function buildCompiledDoc( fmt: E2BDocFormat, referencedImages: ReferencedImageResolution ): Promise { - const { source, fileName, workspaceId, filePrincipal } = args - const cloneDeck = (fmt.ext === 'pptx' || fmt.ext === 'docx') && isSimdocDeckSource(source) - const buffer = cloneDeck - ? await compileDocViaE2BPython( - { - source: wrapSimdocDeckSource(source, fmt.ext as 'pptx' | 'docx'), - fileName, - workspaceId, - filePrincipal, - }, - fmt, - referencedImages - ) - : fmt.engine === 'node' - ? await compileDocViaE2BNode( - { source, fileName, workspaceId, filePrincipal }, - fmt.ext as 'pptx' | 'docx', - referencedImages - ) - : await compileDocViaE2BPython( - { source, fileName, workspaceId, filePrincipal }, - fmt, - referencedImages - ) + const { source, workspaceId, filePrincipal } = args + const stagedInputs = await stageReferencedImages(referencedImages, workspaceId, filePrincipal) + const buffer = await renderPreparedDocument(source, fmt, stagedInputs) await storeCompiledDoc( workspaceId, source, @@ -589,12 +554,32 @@ async function buildCompiledDoc( const contributingFiles = referencedImageIdentities(referencedImages) return { buffer, + artifactKey: compiledArtifactKey( + workspaceId, + source, + fmt.ext, + referencedImages.artifactIdentity + ), contentType: fmt.contentType, dependsOnReferencedFiles: touchesReferencedFiles(args.source, contributingFiles.length), ...(contributingFiles.length > 0 ? { contributingFiles } : {}), } } +async function renderPreparedDocument(source: string, fmt: E2BDocFormat, inputs: SandboxFile[]) { + const cloneDeck = (fmt.ext === 'pptx' || fmt.ext === 'docx') && isSimdocDeckSource(source) + if (cloneDeck) { + return compileDocViaE2BPython( + wrapSimdocDeckSource(source, fmt.ext as 'pptx' | 'docx'), + fmt, + inputs + ) + } + return fmt.engine === 'node' + ? compileDocViaE2BNode(source, fmt.ext as 'pptx' | 'docx', inputs) + : compileDocViaE2BPython(source, fmt, inputs) +} + // Template-clone scripts author against the simdoc Deck (pptx) / Doc (docx) // API. The prelude supplies input_path (staged workspace files) and // OUTPUT_PATH; the finalizer saves the expected variable (scripts never save @@ -750,6 +735,12 @@ export async function compileDoc(args: CompileArgs): Promise const contributingFiles = referencedImageIdentities(referencedImages) return { buffer: existing, + artifactKey: compiledArtifactKey( + workspaceId, + source, + fmt.ext, + referencedImages.artifactIdentity + ), contentType: fmt.contentType, dependsOnReferencedFiles: touchesReferencedFiles(source, contributingFiles.length), ...(contributingFiles.length > 0 ? { contributingFiles } : {}), @@ -763,7 +754,7 @@ export async function compileDoc(args: CompileArgs): Promise * source-keyed artifact. That fallback preserves already-public documents even when their * original inputs no longer resolve; it only reads an existing binary and never executes source. */ -export async function loadCompiledDocByExt( +async function loadCompiledDocByExt( workspaceId: string, source: string, ext: string, @@ -772,7 +763,12 @@ export async function loadCompiledDocByExt( allowPublishedReferencedArtifact?: boolean filePrincipal?: Principal } = {} -): Promise<{ buffer: Buffer; contentType: string } | null> { +): Promise<{ + buffer: Buffer + contentType: string + artifactKey?: string + contributingFiles?: readonly WorkspaceFileSecretProvenanceIdentity[] +} | null> { const fmt = await getE2BDocFormat(`x.${ext}`) if (!fmt) return null const readOptions: CompiledDocReadOptions = { maxBytes: options.maxBytes, signal: options.signal } @@ -780,7 +776,13 @@ export async function loadCompiledDocByExt( if (!options.filePrincipal) { if (referencedFileIds.size === 0) { const buffer = await loadCompiledDoc(workspaceId, source, fmt.ext, undefined, readOptions) - return buffer ? { buffer, contentType: fmt.contentType } : null + return buffer + ? { + buffer, + contentType: fmt.contentType, + artifactKey: compiledArtifactKey(workspaceId, source, fmt.ext), + } + : null } if (options.allowPublishedReferencedArtifact) { const publishedBuffer = await loadPublishedCompiledDoc( @@ -808,7 +810,18 @@ export async function loadCompiledDocByExt( referencedImages.artifactIdentity, readOptions ) - if (buffer) return { buffer, contentType: fmt.contentType } + if (buffer) + return { + buffer, + contentType: fmt.contentType, + artifactKey: compiledArtifactKey( + workspaceId, + source, + fmt.ext, + referencedImages.artifactIdentity + ), + contributingFiles: referencedImageIdentities(referencedImages), + } if (referencedImages.artifactIdentity && options.allowLegacyReferencedArtifact) { const legacyBuffer = await loadCompiledDoc(workspaceId, source, fmt.ext, undefined, readOptions) if (legacyBuffer) return { buffer: legacyBuffer, contentType: fmt.contentType } @@ -820,6 +833,13 @@ function bufferStartsWith(buffer: Buffer, magic: Buffer): boolean { return buffer.length >= magic.length && buffer.subarray(0, magic.length).equals(magic) } +/** Identifies uploaded document binaries so generated source alone enters the compiler. */ +export function isCompiledDocumentBuffer(fileName: string, buffer: Buffer): boolean { + const ext = fileName.slice(fileName.lastIndexOf('.')).toLowerCase() + const magic = COMPILABLE_FORMATS[ext]?.magic ?? (ext === '.xlsx' ? ZIP_MAGIC : undefined) + return magic !== undefined && bufferStartsWith(buffer, magic) +} + /** * How a read-only consumer (e.g. the public share route) should serve a stored doc: * - `passthrough` — serve the raw stored bytes as-is (a non-doc file, or an uploaded @@ -829,7 +849,7 @@ function bufferStartsWith(buffer: Buffer, magic: Buffer): boolean { * not exist yet; the raw bytes are source, so serving them under the file's binary * content type would be corrupt. The caller should signal "not ready" instead. */ -export type ServableDoc = +type ServableDoc = | { kind: 'passthrough' } | { kind: 'artifact'; buffer: Buffer; contentType: string } | { kind: 'unavailable' } @@ -919,11 +939,10 @@ export async function resolveServableDocBytes(args: { // xlsx isn't in COMPILABLE_FORMATS (no isolated-vm path), so match its ZIP magic // explicitly alongside the table-driven formats. - const magic = format?.magic ?? (extNoDot === 'xlsx' ? ZIP_MAGIC : undefined) - if (magic && bufferStartsWith(rawBuffer, magic)) { + if (isCompiledDocumentBuffer(fileName, rawBuffer)) { return { buffer: rawBuffer, - contentType: getContentType(fileName), + contentType: getMimeTypeFromExtension(getFileExtension(fileName)), dependsOnReferencedFiles: false, } } @@ -931,7 +950,7 @@ export async function resolveServableDocBytes(args: { if (!format && extNoDot !== 'xlsx') { return { buffer: rawBuffer, - contentType: getContentType(fileName), + contentType: getMimeTypeFromExtension(getFileExtension(fileName)), dependsOnReferencedFiles: false, } } diff --git a/apps/sim/lib/mothership/tools/server/files/doc-compiled-store.test.ts b/apps/sim/lib/uploads/documents/compiled-store.test.ts similarity index 99% rename from apps/sim/lib/mothership/tools/server/files/doc-compiled-store.test.ts rename to apps/sim/lib/uploads/documents/compiled-store.test.ts index b412d03e45d..fd9241a6216 100644 --- a/apps/sim/lib/mothership/tools/server/files/doc-compiled-store.test.ts +++ b/apps/sim/lib/uploads/documents/compiled-store.test.ts @@ -8,7 +8,7 @@ import { loadCompiledDoc, loadPublishedCompiledDoc, storeCompiledDoc, -} from '@/lib/mothership/tools/server/files/doc-compiled-store' +} from '@/lib/uploads/documents/compiled-store' import { MAX_BUFFERED_TRANSFER_BYTES } from '@/lib/uploads/shared/types' const { mockDownloadFile, mockUploadFile } = storageServiceMockFns diff --git a/apps/sim/lib/mothership/tools/server/files/doc-compiled-store.ts b/apps/sim/lib/uploads/documents/compiled-store.ts similarity index 78% rename from apps/sim/lib/mothership/tools/server/files/doc-compiled-store.ts rename to apps/sim/lib/uploads/documents/compiled-store.ts index e98342a01e1..8495b9c33f1 100644 --- a/apps/sim/lib/mothership/tools/server/files/doc-compiled-store.ts +++ b/apps/sim/lib/uploads/documents/compiled-store.ts @@ -5,6 +5,7 @@ import { isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' import { isObjectNotFoundError } from '@/lib/uploads/core/errors' import { downloadFile, uploadFile } from '@/lib/uploads/core/storage-service' import { MAX_BUFFERED_TRANSFER_BYTES } from '@/lib/uploads/shared/types' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' const logger = createLogger('CopilotDocCompiledStore') @@ -13,15 +14,15 @@ const logger = createLogger('CopilotDocCompiledStore') * * The Python doc path keeps the SOURCE as the primary file (the agent reads and * edits it exactly like the JS path). The compiled binary is stored as its own - * S3 object, content-addressed by (workspaceId, sha256(source + referenced-input identity), ext) — + * S3 object, content-addressed by (owner, sha256(source + referenced-input identity), ext) — * the hash is in the key, so source or referenced-file content changes invalidate the artifact. Every read path * (serve, preview, /compiled) loads the artifact for the current source hash and * recompiles only when it is absent. No fileId in the key means any site with * the source (e.g. the serve route) can find it. S3 is cheap; stale artifacts * are inert. */ -function compiledArtifactKey( - workspaceId: string, +export function compiledArtifactKey( + owner: string | EditableFileOwner, source: string, ext: string, referencedInputIdentity?: string @@ -30,12 +31,20 @@ function compiledArtifactKey( ? JSON.stringify({ version: 1, source, referencedInputIdentity }) : source const hash = createHash('sha256').update(cacheInput, 'utf-8').digest('hex') - return `copilot-doc-compiled/${workspaceId}/${hash}.${ext}` + return typeof owner === 'string' || owner.entityType === 'workspace' + ? `copilot-doc-compiled/${typeof owner === 'string' ? owner : owner.entityId}/${hash}.${ext}` + : `project/${owner.entityId}/compiled/${hash}.${ext}` } -function publishedArtifactPointerKey(workspaceId: string, source: string, ext: string): string { +function publishedArtifactPointerKey( + owner: string | EditableFileOwner, + source: string, + ext: string +): string { const sourceHash = createHash('sha256').update(source, 'utf-8').digest('hex') - return `copilot-doc-compiled/${workspaceId}/${sourceHash}.${ext}.published.json` + return typeof owner === 'string' || owner.entityType === 'workspace' + ? `copilot-doc-compiled/${typeof owner === 'string' ? owner : owner.entityId}/${sourceHash}.${ext}.published.json` + : `project/${owner.entityId}/compiled/${sourceHash}.${ext}.published.json` } export interface CompiledDocReadOptions { @@ -57,7 +66,7 @@ async function loadPublishedArtifactPointer( try { encoded = await downloadFile({ key, - context: 'copilot', + context: key.startsWith('project/') ? 'project' : 'copilot', maxBytes: Math.min( options.maxBytes ?? MAX_BUFFERED_TRANSFER_BYTES, MAX_BUFFERED_TRANSFER_BYTES @@ -106,17 +115,17 @@ async function loadPublishedArtifactPointer( * that is too large would retry forever behind that. */ export async function loadCompiledDoc( - workspaceId: string, + owner: string | EditableFileOwner, source: string, ext: string, referencedInputIdentity?: string, options: CompiledDocReadOptions = {} ): Promise { - const key = compiledArtifactKey(workspaceId, source, ext, referencedInputIdentity) + const key = compiledArtifactKey(owner, source, ext, referencedInputIdentity) try { return await downloadFile({ key, - context: 'copilot', + context: key.startsWith('project/') ? 'project' : 'copilot', maxBytes: Math.min( options.maxBytes ?? MAX_BUFFERED_TRANSFER_BYTES, MAX_BUFFERED_TRANSFER_BYTES @@ -136,24 +145,26 @@ export async function loadCompiledDoc( * files or compile arbitrary source themselves. */ export async function publishCompiledDocArtifact( - workspaceId: string, + owner: string | EditableFileOwner, source: string, ext: string, - referencedInputIdentity: string + referencedInputIdentity: string, + onArtifactWrite?: (key: string) => void ): Promise { if (!referencedInputIdentity) { throw new Error('Published compiled document identity must not be empty') } - const key = publishedArtifactPointerKey(workspaceId, source, ext) + const key = publishedArtifactPointerKey(owner, source, ext) const existing = await loadPublishedArtifactPointer(key) if (existing?.referencedInputIdentity === referencedInputIdentity) return const pointer: PublishedArtifactPointer = { version: 1, referencedInputIdentity } try { + onArtifactWrite?.(key) await uploadFile({ file: Buffer.from(JSON.stringify(pointer), 'utf-8'), fileName: `doc.${ext}.published.json`, contentType: 'application/json', - context: 'copilot', + context: key.startsWith('project/') ? 'project' : 'copilot', customKey: key, preserveKey: true, }) @@ -168,16 +179,16 @@ export async function publishCompiledDocArtifact( /** Loads an artifact previously published by an authorized compile, or null before cutover. */ export async function loadPublishedCompiledDoc( - workspaceId: string, + owner: string | EditableFileOwner, source: string, ext: string, options: CompiledDocReadOptions = {} ): Promise { - const key = publishedArtifactPointerKey(workspaceId, source, ext) + const key = publishedArtifactPointerKey(owner, source, ext) const pointer = await loadPublishedArtifactPointer(key, options) if (!pointer) return null const artifact = await loadCompiledDoc( - workspaceId, + owner, source, ext, pointer.referencedInputIdentity, @@ -196,25 +207,27 @@ export async function loadPublishedCompiledDoc( * write fail honestly so the caller (and the agent) can retry. */ export async function storeCompiledDoc( - workspaceId: string, + owner: string | EditableFileOwner, source: string, ext: string, contentType: string, binary: Buffer, - referencedInputIdentity?: string + referencedInputIdentity?: string, + onArtifactWrite?: (key: string) => void ): Promise { - const key = compiledArtifactKey(workspaceId, source, ext, referencedInputIdentity) + const key = compiledArtifactKey(owner, source, ext, referencedInputIdentity) try { + onArtifactWrite?.(key) await uploadFile({ file: binary, fileName: `doc.${ext}`, contentType, - context: 'copilot', + context: key.startsWith('project/') ? 'project' : 'copilot', customKey: key, preserveKey: true, }) if (referencedInputIdentity) { - await publishCompiledDocArtifact(workspaceId, source, ext, referencedInputIdentity) + await publishCompiledDocArtifact(owner, source, ext, referencedInputIdentity, onArtifactWrite) } } catch (err) { logger.error('Failed to store compiled doc artifact', { diff --git a/apps/sim/lib/uploads/documents/index.ts b/apps/sim/lib/uploads/documents/index.ts new file mode 100644 index 00000000000..c4f537779fe --- /dev/null +++ b/apps/sim/lib/uploads/documents/index.ts @@ -0,0 +1,6 @@ +export { + collectReferencedFileIds, + getE2BDocFormat, + isCompiledDocumentBuffer, +} from './compile' +export { resolveDocumentRender } from './render' diff --git a/apps/sim/lib/uploads/documents/input-identity.ts b/apps/sim/lib/uploads/documents/input-identity.ts new file mode 100644 index 00000000000..4074d06d5f8 --- /dev/null +++ b/apps/sim/lib/uploads/documents/input-identity.ts @@ -0,0 +1,21 @@ +import type { WorkspaceFileRow } from '@sim/db/schema' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' + +/** Binds a rendered cache entry to every authorized input revision, in canonical file order. */ +export function fileDocumentInputIdentity( + owner: EditableFileOwner, + files: readonly WorkspaceFileRow[] +) { + return files.length > 0 + ? JSON.stringify({ + version: 1, + inputs: files.map((file) => ({ + fileId: file.id, + key: file.key, + context: owner.entityType, + contentVersion: file.contentUpdatedAt.toISOString(), + size: file.sizeBytes, + })), + }) + : undefined +} diff --git a/apps/sim/lib/mothership/tools/server/files/pptx-shim.ts b/apps/sim/lib/uploads/documents/pptx-shim.ts similarity index 100% rename from apps/sim/lib/mothership/tools/server/files/pptx-shim.ts rename to apps/sim/lib/uploads/documents/pptx-shim.ts diff --git a/apps/sim/lib/uploads/documents/references.ts b/apps/sim/lib/uploads/documents/references.ts new file mode 100644 index 00000000000..dc0ede9044e --- /dev/null +++ b/apps/sim/lib/uploads/documents/references.ts @@ -0,0 +1,21 @@ +interface DocumentFileReference { + fileId: string + start: number + end: number +} + +const INPUT_PATH_RE = /\/home\/user\/inputs\/([A-Za-z0-9_-]+)/g +const FILE_HELPER_RE = + /\b(?:getFileBase64|addImage|drawImage|input_path)\(\s*(?:[A-Za-z_$][\w$]*\s*,\s*)?['"]([A-Za-z0-9_-]+)['"]/g + +/** Shares the compiler's static image/template grammar with source copies; bare text is never a file reference. */ +export function* iterateDocumentFileReferences(source: string): Generator { + for (const pattern of [INPUT_PATH_RE, FILE_HELPER_RE]) { + for (const match of source.matchAll(pattern)) { + const fileId = match[1] + if (!fileId) continue + const start = match.index + match[0].lastIndexOf(fileId) + yield { fileId, start, end: start + fileId.length } + } + } +} diff --git a/apps/sim/lib/uploads/documents/render.ts b/apps/sim/lib/uploads/documents/render.ts new file mode 100644 index 00000000000..cbd852bdf2e --- /dev/null +++ b/apps/sim/lib/uploads/documents/render.ts @@ -0,0 +1,35 @@ +import { OrchestrationError } from '@/lib/core/orchestration/types' +import { isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' +import { docNotReadyMessage, isDocNotReadyError } from '@/lib/uploads/utils/doc-not-ready' +import { formatFileSize } from '@/lib/uploads/utils/file-utils' + +/** Preserves retryable generation and output-size errors across authorized artifact readers. */ +export async function resolveDocumentRender( + fileName: string, + options: { maxBytes: number; signal?: AbortSignal; tooLargeMessage?: (limit: string) => string }, + render: () => Promise +): Promise { + try { + options.signal?.throwIfAborted() + return await render() + } catch (error) { + options.signal?.throwIfAborted() + if (isDocNotReadyError(error)) { + throw new OrchestrationError( + 'conflict', + error.pending + ? docNotReadyMessage() + : `"${fileName}" could not be generated: ${error.message}` + ) + } + if (isPayloadSizeLimitError(error)) { + const limit = formatFileSize(options.maxBytes, { includeBytes: true }) + throw new OrchestrationError( + 'payload_too_large', + options.tooLargeMessage?.(limit) ?? + `"${fileName}" renders to more than ${limit} and is too large to download.` + ) + } + throw error + } +} diff --git a/apps/sim/lib/mothership/tools/server/files/doc-servable.test.ts b/apps/sim/lib/uploads/documents/servable.test.ts similarity index 97% rename from apps/sim/lib/mothership/tools/server/files/doc-servable.test.ts rename to apps/sim/lib/uploads/documents/servable.test.ts index d2207e71c94..8505ce1a297 100644 --- a/apps/sim/lib/mothership/tools/server/files/doc-servable.test.ts +++ b/apps/sim/lib/uploads/documents/servable.test.ts @@ -33,23 +33,19 @@ vi.mock('@/lib/workspace-files/application/read-workspace-file-content', () => ( vi.mock('@/lib/workspace-files/application/read-workspace-file-metadata', () => ({ readWorkspaceFileMetadata: { execute: mockReadWorkspaceFileMetadata }, })) -vi.mock('./doc-compiled-store', () => ({ +vi.mock('@/lib/uploads/documents/compiled-store', () => ({ loadCompiledDoc: mockLoadCompiledDoc, loadPublishedCompiledDoc: mockLoadPublishedCompiledDoc, publishCompiledDocArtifact: mockPublishCompiledDocArtifact, storeCompiledDoc: mockStoreCompiledDoc, })) -vi.mock('@/app/api/files/utils', () => ({ - getContentType: (name: string) => - name.endsWith('.pdf') - ? 'application/pdf' - : name.endsWith('.txt') - ? 'text/plain' - : 'application/octet-stream', -})) -import { DocCompileUserError } from '@/lib/mothership/tools/server/files/doc-compile-error' -import { compileDoc, resolveServableDoc, resolveServableDocBytes } from './doc-compile' +import { + compileDoc, + resolveServableDoc, + resolveServableDocBytes, +} from '@/lib/uploads/documents/compile' +import { DocCompileUserError } from '@/lib/uploads/documents/compile-error' const { mockExecuteInSandbox } = remoteSandboxMockFns diff --git a/apps/sim/lib/uploads/providers/google-cloud-storage/client.ts b/apps/sim/lib/uploads/providers/google-cloud-storage/client.ts index 3a2061d15d1..5dfa7c0cfca 100644 --- a/apps/sim/lib/uploads/providers/google-cloud-storage/client.ts +++ b/apps/sim/lib/uploads/providers/google-cloud-storage/client.ts @@ -535,8 +535,11 @@ async function gcsXmlApiRequest( if (!response.ok) { const errorBody = await response.text().catch(() => '') - throw new Error( - `GCS XML API ${method} ${query.split('&')[0]} failed for ${key}: ${response.status} ${response.statusText}${errorBody ? ` – ${errorBody.slice(0, 500)}` : ''}` + throw Object.assign( + new Error( + `GCS XML API ${method} ${query.split('&')[0]} failed for ${key}: ${response.status} ${response.statusText}${errorBody ? ` – ${errorBody.slice(0, 500)}` : ''}` + ), + { statusCode: response.status } ) } diff --git a/apps/sim/lib/uploads/shared/types.ts b/apps/sim/lib/uploads/shared/types.ts index ed67609ef2b..7c7b6edb882 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' + | 'project' | 'table-import' | 'profile-pictures' | 'og-images' diff --git a/apps/sim/lib/uploads/upload-session/presenter.ts b/apps/sim/lib/uploads/upload-session/presenter.ts new file mode 100644 index 00000000000..45a3dbd4fbc --- /dev/null +++ b/apps/sim/lib/uploads/upload-session/presenter.ts @@ -0,0 +1,15 @@ +import { v2UploadStatusSchema } from '@/lib/api/contracts/v2/uploads' +import type { UploadSessionRecord } from '@/lib/uploads/upload-session/service' + +/** Shared transfer state excludes private credential, provenance and storage bindings. */ +export function presentUploadSessionState(session: UploadSessionRecord) { + return { + id: session.id, + status: v2UploadStatusSchema.parse(session.status), + name: session.fileName, + contentType: session.contentType, + size: session.fileSize, + expiresAt: session.expiresAt.toISOString(), + error: session.error, + } +} diff --git a/apps/sim/lib/uploads/upload-session/registration-lifetime.integration.ts b/apps/sim/lib/uploads/upload-session/registration-lifetime.integration.ts new file mode 100644 index 00000000000..b3adc1224c1 --- /dev/null +++ b/apps/sim/lib/uploads/upload-session/registration-lifetime.integration.ts @@ -0,0 +1,154 @@ +import { mkdtempSync } from 'node:fs' +import { mkdir, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { dirname, join } from 'node:path' +import { db } from '@sim/db' +import { uploadSession } from '@sim/db/schema' +import { createDeferred } from '@sim/testing/helpers/deferred' +import { setUploadDirServer, uploadsSetupMock } from '@sim/testing/mocks/uploads-setup.mock' +import { getErrorMessage } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import { eq, inArray, sql } from 'drizzle-orm' +import { afterAll, describe, expect, it, vi } from 'vitest' + +vi.mock('@/lib/uploads/core/setup.server', () => uploadsSetupMock) + +import { writeLocalPutObject } from '@/lib/uploads/upload-session/provider' +import { + abortUploadSession, + cleanupExpiredUploadSessions, + createUploadSession, + getOwnedUploadSession, +} from '@/lib/uploads/upload-session/service' + +const storageRoot = mkdtempSync(join(tmpdir(), 'sim-upload-registration-')) +setUploadDirServer(storageRoot) +const uploadIds: string[] = [] +const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = + [] + +function check(name: string, run: () => Promise) { + it(name, async () => { + const started = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - started }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - started, + error: getErrorMessage(error), + }) + throw error + } + }) +} + +async function fixture() { + const created = await createUploadSession({ + purpose: 'profile_picture', + userId: generateId(), + fileName: 'test.png', + contentType: 'image/png', + fileSize: 4, + localOrigin: 'http://localhost:3000', + }) + uploadIds.push(created.id) + await writeLocalPutObject({ + uploadId: created.id, + key: created.finalKey, + expectedSize: 4, + contentType: created.contentType, + metadata: {}, + body: new ReadableStream({ + start(controller) { + controller.enqueue(Buffer.from('file')) + controller.close() + }, + }), + }) + await db + .update(uploadSession) + .set({ status: 'finalizing', expiresAt: new Date(Date.now() - 1000) }) + .where(eq(uploadSession.id, created.id)) + return getOwnedUploadSession({ uploadId: created.id, uploadToken: created.uploadToken }) +} + +describe('registered upload byte lifetime', () => { + check('a stale abort cannot claim a session after its file has been registered', async () => { + const stale = await fixture() + await db + .update(uploadSession) + .set({ completedFileId: generateId() }) + .where(eq(uploadSession.id, stale.id)) + await expect(abortUploadSession(stale)).rejects.toMatchObject({ code: 'conflict' }) + expect(await readFile(join(storageRoot, stale.finalKey), 'utf8')).toBe('file') + const [current] = await db.select().from(uploadSession).where(eq(uploadSession.id, stale.id)) + expect(current.status).toBe('finalizing') + expect(current.completedFileId).not.toBeNull() + }) + + check( + 'expiry rechecks registration when its candidate waits behind the committing transaction', + async () => { + const session = await fixture() + const locked = createDeferred() + const release = createDeferred() + const holder = db.transaction(async (tx) => { + await tx + .select({ id: uploadSession.id }) + .from(uploadSession) + .where(eq(uploadSession.id, session.id)) + .for('update') + locked.resolve() + await release.promise + await tx + .update(uploadSession) + .set({ completedFileId: generateId() }) + .where(eq(uploadSession.id, session.id)) + }) + await locked.promise + const cleanup = cleanupExpiredUploadSessions() + try { + let waiting = false + for (let attempt = 0; attempt < 100; attempt++) { + const rows = await db.execute( + sql`SELECT 1 FROM pg_stat_activity WHERE datname = current_database() AND wait_event_type = 'Lock' AND query LIKE 'update "upload_session"%'` + ) + if (rows.length) { + waiting = true + break + } + await sleep(10) + } + expect( + waiting, + 'expiry must have selected the old candidate and be waiting to claim it' + ).toBe(true) + } finally { + release.resolve() + await holder + } + await cleanup + expect(await readFile(join(storageRoot, session.finalKey), 'utf8')).toBe('file') + const [current] = await db + .select() + .from(uploadSession) + .where(eq(uploadSession.id, session.id)) + expect(current.status).toBe('finalizing') + expect(current.completedFileId).not.toBeNull() + } + ) +}) + +afterAll(async () => { + const reportPath = + process.env.UPLOAD_REGISTRATION_REPORT_PATH ?? 'test-results/upload-registration.json' + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, `${JSON.stringify({ checks }, null, 2)}\n`) + if (uploadIds.length) await db.delete(uploadSession).where(inArray(uploadSession.id, uploadIds)) + await rm(storageRoot, { recursive: true, force: true }) + await db.$client.end() +}) diff --git a/apps/sim/lib/uploads/upload-session/service.ts b/apps/sim/lib/uploads/upload-session/service.ts index 71fb36f683f..02b4ae48143 100644 --- a/apps/sim/lib/uploads/upload-session/service.ts +++ b/apps/sim/lib/uploads/upload-session/service.ts @@ -604,6 +604,8 @@ export function assertUploadSessionAuthBinding( session: UploadSessionRecord, principal: Principal ): void { + if (session.purpose === 'project_file') + throw new UploadSessionError('forbidden', 'Project upload control is unavailable') if (session.purpose === 'organization_logo') { assertOrganizationLogoControlBinding(session, principal) return @@ -1225,6 +1227,7 @@ async function claimSession( and( eq(uploadSession.id, id), inArray(uploadSession.status, statuses), + ...(nextStatus === 'aborting' ? [isNull(uploadSession.completedFileId)] : []), or( isNull(uploadSession.processingLeaseId), isNull(uploadSession.processingLeaseExpiresAt), @@ -1436,6 +1439,7 @@ function copilotUploadAudience(purpose: UploadSessionPurpose): string | undefine function isPrincipalBoundUploadPurpose(purpose: UploadSessionPurpose): boolean { return ( purpose === 'workspace_file' || + purpose === 'project_file' || purpose === 'knowledge_document' || purpose === 'table_import' || purpose === 'mothership_attachment' || @@ -1507,6 +1511,7 @@ function resolveUploadStorage( function isStorageContext(value: string): value is StorageContext { return [ 'workspace', + 'project', 'table-import', 'knowledge-base', 'profile-pictures', diff --git a/apps/sim/lib/uploads/upload-session/types.ts b/apps/sim/lib/uploads/upload-session/types.ts index e418047a3dc..24e823d3c90 100644 --- a/apps/sim/lib/uploads/upload-session/types.ts +++ b/apps/sim/lib/uploads/upload-session/types.ts @@ -1,5 +1,9 @@ +/** Persisted metadata key shared by credential binding and retired-upload cleanup. */ +export const PROJECT_FILE_UPLOAD_BINDING_KEY = 'projectFileAuthBinding' + export type UploadSessionPurpose = | 'workspace_file' + | 'project_file' | 'table_import' | 'knowledge_document' | 'profile_picture' diff --git a/apps/sim/lib/uploads/utils/doc-not-ready.ts b/apps/sim/lib/uploads/utils/doc-not-ready.ts index 2e2ed54cc07..3b9d9335559 100644 --- a/apps/sim/lib/uploads/utils/doc-not-ready.ts +++ b/apps/sim/lib/uploads/utils/doc-not-ready.ts @@ -1,4 +1,4 @@ -import { DocCompileUserError } from '@/lib/mothership/tools/server/files/doc-compile-error' +import { DocCompileUserError } from '@/lib/uploads/documents/compile-error' /** True when `error` means a generated document's artifact is still compiling. */ export function isDocNotReadyError(error: unknown): error is DocCompileUserError { diff --git a/apps/sim/lib/uploads/utils/file-utils.server.test.ts b/apps/sim/lib/uploads/utils/file-utils.server.test.ts index d4cf63ca846..920dbb2b757 100644 --- a/apps/sim/lib/uploads/utils/file-utils.server.test.ts +++ b/apps/sim/lib/uploads/utils/file-utils.server.test.ts @@ -20,7 +20,7 @@ vi.mock('@/lib/uploads/contexts/execution/execution-file-manager', () => ({ vi.mock('@/lib/uploads/contexts/workspace/workspace-file-manager', () => workspaceFileManagerMock) -vi.mock('@/lib/mothership/tools/server/files/doc-compile', () => ({ +vi.mock('@/lib/uploads/documents/compile', () => ({ resolveServableDocBytes: mockResolveServableDocBytes, })) diff --git a/apps/sim/lib/uploads/utils/file-utils.server.ts b/apps/sim/lib/uploads/utils/file-utils.server.ts index 555339bb125..8d3aff25dbb 100644 --- a/apps/sim/lib/uploads/utils/file-utils.server.ts +++ b/apps/sim/lib/uploads/utils/file-utils.server.ts @@ -412,6 +412,7 @@ export async function downloadFileFromStorage( * actually attach/upload, plus the content type that matches those bytes. */ export interface ServableFile { + artifactKey?: string buffer: Buffer contentType: string contributingFiles?: readonly WorkspaceFileSecretProvenanceIdentity[] @@ -491,9 +492,7 @@ export async function downloadServableFileFromStorage( undefined) : undefined - const { resolveServableDocBytes } = await import( - '@/lib/mothership/tools/server/files/doc-compile' - ) + const { resolveServableDocBytes } = await import('@/lib/uploads/documents/compile') const resolved = await resolveServableDocBytes({ rawBuffer: buffer, fileName: userFile.name, diff --git a/apps/sim/lib/users/account-deletion.ts b/apps/sim/lib/users/account-deletion.ts index ce66288f5d4..de7c04c1859 100644 --- a/apps/sim/lib/users/account-deletion.ts +++ b/apps/sim/lib/users/account-deletion.ts @@ -36,6 +36,7 @@ import { } from '@/lib/table/rows/executions' import type { StorageContext } from '@/lib/uploads' import { isUsingCloudStorage, StorageService } from '@/lib/uploads' +import { processWorkspaceFileStorageCleanupsNow } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' import { reassignBilledAccountForUser, reassignOwnedWorkspacesForUser, @@ -691,9 +692,11 @@ export async function deleteUserAccount(userId: string): Promise { - await prepareProjectsForAccountDeletion(tx, userId, doomedWorkspaceIds) + const projectCleanup = await prepareProjectsForAccountDeletion(tx, userId, doomedWorkspaceIds) + projectStorageCleanupEventIds = projectCleanup.storageCleanupEventIds if (doomedWorkspaceIds.length > 0) { /** * Re-checked here rather than trusted from the plan: a workspace that @@ -822,6 +825,11 @@ export async function deleteUserAccount(userId: string): Promise { - return downloadServableFileFromStorage( + const result = await downloadServableFileFromStorage( { id: fileRecord.id, name: fileRecord.name, @@ -32,4 +34,28 @@ export async function fetchAuthorizedServableWorkspaceFileBuffer( filePrincipal, } ) + const artifactKey = result.artifactKey + if (artifactKey && result.contributingFiles?.every((file) => file.contentUpdatedAt) !== false) { + await db.transaction((tx) => + markFileSearchArtifactReadyInTx(tx, { + owner: { entityType: 'workspace', entityId: fileRecord.workspaceId }, + file: { + fileId: fileRecord.id, + key: fileRecord.key, + contentUpdatedAt: fileRecord.contentUpdatedAt ?? fileRecord.updatedAt, + }, + dependencies: (result.contributingFiles ?? []).map((file) => { + if (!file.contentUpdatedAt || file.context !== 'workspace') + throw new Error('Artifact input has no canonical workspace revision') + return { + fileId: file.fileId, + key: file.key, + sourceContentUpdatedAt: file.contentUpdatedAt, + } + }), + artifactKey, + }) + ) + } + return result } diff --git a/apps/sim/lib/workspace-files/application/list-workspace-files.ts b/apps/sim/lib/workspace-files/application/list-workspace-files.ts index 57a82ed0718..f4721a4df7a 100644 --- a/apps/sim/lib/workspace-files/application/list-workspace-files.ts +++ b/apps/sim/lib/workspace-files/application/list-workspace-files.ts @@ -3,7 +3,7 @@ import { OrchestrationError } from '@/lib/core/orchestration/types' import { MAX_FOLDERS_PER_WORKSPACE } from '@/lib/folders/constants' import { loadActiveFolderPathIndex, resolveFolderPathFilter } from '@/lib/folders/queries' import type { FolderIdScope } from '@/lib/folders/scope' -import { collectDescendantFolderIdsFrom, indexFolderChildren } from '@/lib/folders/subtree' +import { resolveFolderScope } from '@/lib/folders/subtree' import { getWorkspaceShares } from '@/lib/public-shares/share-manager' import { listWorkspaceFiles, @@ -46,30 +46,6 @@ export interface ListWorkspaceFilesInFolderScopeInput { limit: number } -/** - * Which folders a page covers, in the shape {@link queryWorkspaceFiles} takes: one id, `null` - * for the workspace root, several ids for a subtree, or `undefined` for the whole workspace. - * - * `unfiltered` (no `folderPath`) and a recursive filter on the root both mean the whole - * workspace, so both drop the folder predicate. A recursive filter on a real folder names - * every folder in its subtree, which the query takes as one `IN (...)` over the index already - * loaded for the path lookup — no second read, and no recursive CTE. - */ -function resolveFolderScope( - folderIndex: Awaited>, - folderFilter: ReturnType, - recursive: boolean | undefined -): string | null | string[] | undefined { - if (folderFilter.kind !== 'folder') return undefined - if (!recursive) return folderFilter.folderId - if (folderFilter.folderId === null) return undefined - const childrenByParent = indexFolderChildren(folderIndex.rowById.values()) - return [ - folderFilter.folderId, - ...collectDescendantFolderIdsFrom(childrenByParent, folderFilter.folderId), - ] -} - async function resolveListWorkspaceFileContext(workspaceId: string) { const workspace = await loadActiveWorkspaceContext(workspaceId) if (!workspace) throw new OrchestrationError('not_found', 'Workspace not found') diff --git a/apps/sim/lib/workspace-files/application/resolve-rendered-workspace-artifact.ts b/apps/sim/lib/workspace-files/application/resolve-rendered-workspace-artifact.ts index 889b8332789..6d1728e9ece 100644 --- a/apps/sim/lib/workspace-files/application/resolve-rendered-workspace-artifact.ts +++ b/apps/sim/lib/workspace-files/application/resolve-rendered-workspace-artifact.ts @@ -1,9 +1,6 @@ import type { Principal } from '@sim/auth/principal' -import { OrchestrationError } from '@/lib/core/orchestration/types' -import { isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace' -import { docNotReadyMessage, isDocNotReadyError } from '@/lib/uploads/utils/doc-not-ready' -import { formatFileSize } from '@/lib/uploads/utils/file-utils' +import { resolveDocumentRender } from '@/lib/uploads/documents' import { fetchAuthorizedServableWorkspaceFileBuffer } from '@/lib/workspace-files/application/fetch-servable-workspace-file-buffer' /** @@ -32,29 +29,10 @@ export async function resolveRenderedWorkspaceArtifact( filePrincipal: Principal, options: { maxBytes: number; signal?: AbortSignal; tooLargeMessage?: (limit: string) => string } ): Promise<{ buffer: Buffer; contentType: string }> { - try { - options.signal?.throwIfAborted() - return await fetchAuthorizedServableWorkspaceFileBuffer(file, filePrincipal, { + return resolveDocumentRender(file.name, options, () => + fetchAuthorizedServableWorkspaceFileBuffer(file, filePrincipal, { maxBytes: options.maxBytes, signal: options.signal, }) - } catch (error) { - options.signal?.throwIfAborted() - if (isDocNotReadyError(error)) { - if (error.pending) throw new OrchestrationError('conflict', docNotReadyMessage()) - throw new OrchestrationError( - 'conflict', - `"${file.name}" could not be generated: ${error.message}` - ) - } - if (isPayloadSizeLimitError(error)) { - const limit = formatFileSize(options.maxBytes, { includeBytes: true }) - throw new OrchestrationError( - 'payload_too_large', - options.tooLargeMessage?.(limit) ?? - `"${file.name}" renders to more than ${limit} and is too large to download.` - ) - } - throw error - } + ) } diff --git a/apps/sim/lib/workspace-files/application/search-workspace-file-content.test.ts b/apps/sim/lib/workspace-files/application/search-workspace-file-content.test.ts index 96a9cf85eb7..d5f47ca63ca 100644 --- a/apps/sim/lib/workspace-files/application/search-workspace-file-content.test.ts +++ b/apps/sim/lib/workspace-files/application/search-workspace-file-content.test.ts @@ -13,7 +13,8 @@ const hoisted = vi.hoisted(() => ({ vi.mock('@sim/platform-authz/workspace', () => workspaceAuthzMock) vi.mock('@/lib/uploads/contexts/workspace', () => workspaceUploadsMock) vi.mock('@/lib/workspace-files/search/repository', () => ({ - searchWorkspaceFileIndex: hoisted.search, + searchFileIndex: hoisted.search, + toWorkspaceFileSearchResult: vi.fn(), })) vi.mock('@/lib/workspace-files/resolve-folder-scope', () => ({ resolveWorkspaceFolderScope: hoisted.folders, diff --git a/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts index 0c6e97ef267..c8fcb2b1c69 100644 --- a/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts @@ -1,15 +1,29 @@ +import { db } from '@sim/db' +import { workspace } from '@sim/db/schema' +import { and, eq, isNull } from 'drizzle-orm' import { OrchestrationError } from '@/lib/core/orchestration/types' -import { loadActiveWorkspaceContext } from '@/lib/uploads/contexts/workspace' +import { acquirePermissionGroupOrgLock } from '@/lib/permission-groups/locks' +import { lockWorkspaceProject } from '@/lib/projects/membership' +import { listFileFolders, loadActiveWorkspaceContext } from '@/lib/uploads/contexts/workspace' +import { authorizeWorkspaceFileAccess } from '@/lib/workspace-files/application/authorization' import { defineAuthorizedWorkspaceFileUseCase } from '@/lib/workspace-files/application/authorized-workspace-file-use-case' +import { reportWorkspaceFileDelivery } from '@/lib/workspace-files/application/file-delivery-observer' import { fileOperations } from '@/lib/workspace-files/application/operations' import { resolveWorkspaceFolderScope } from '@/lib/workspace-files/resolve-folder-scope' +import { + loadFileSearchDelivery, + resolveFileSearchFolderScope, +} from '@/lib/workspace-files/search/delivery' import { WorkspaceFileSearchUnavailableError } from '@/lib/workspace-files/search/errors' import { compileFileSearchPattern, type FileSearchMode, FileSearchPatternError, } from '@/lib/workspace-files/search/pattern' -import { searchWorkspaceFileIndex } from '@/lib/workspace-files/search/repository' +import { + searchFileIndex, + toWorkspaceFileSearchResult, +} from '@/lib/workspace-files/search/repository' export interface SearchWorkspaceFileContentInput { workspaceId: string @@ -61,13 +75,42 @@ export const searchWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ signal?.throwIfAborted() try { - return await searchWorkspaceFileIndex({ - workspaceId: context.workspaceId, + const owner = { entityType: 'workspace' as const, entityId: context.workspaceId } + const result = await searchFileIndex({ + owner, pattern: compileFileSearchPattern(input.query, input.mode), maxResults: input.maxResults, folderScope, signal, }) + const provenance = await db.transaction(async (tx) => { + await lockWorkspaceProject(tx, context.workspaceId) + const [current] = await tx + .select({ + workspaceId: workspace.id, + workspaceOrganizationId: workspace.organizationId, + allowPersonalApiKeys: workspace.allowPersonalApiKeys, + }) + .from(workspace) + .where(and(eq(workspace.id, context.workspaceId), isNull(workspace.archivedAt))) + .for('share') + if (!current) throw new OrchestrationError('not_found', 'Workspace not found') + if (current.workspaceOrganizationId) + await acquirePermissionGroupOrgLock(tx, current.workspaceOrganizationId) + await authorizeWorkspaceFileAccess(principal, fileOperations.searchContent, current, { + executor: tx, + forUpdate: true, + }) + const folders = input.folderPaths === undefined ? [] : await listFileFolders(owner, {}, tx) + return loadFileSearchDelivery(tx, { + owner, + prepared: result, + scope: resolveFileSearchFolderScope(folders, input), + signal, + }) + }) + await reportWorkspaceFileDelivery(provenance) + return toWorkspaceFileSearchResult(result) } catch (error) { /** * A rejected or too-expensive pattern is the caller's to fix, and the diff --git a/apps/sim/lib/workspace-files/owner-adapters.ts b/apps/sim/lib/workspace-files/owner-adapters.ts new file mode 100644 index 00000000000..27f8ac5622b --- /dev/null +++ b/apps/sim/lib/workspace-files/owner-adapters.ts @@ -0,0 +1,24 @@ +import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { FileOwner } from '@/lib/workspace-files/ownership' + +/** Each authority boundary registers only the owners whose policy it implements. */ +export type FileOwnerAdapters = Readonly>> + +/** Owner identity never enables an operation without an explicitly registered adapter. */ +export function requireFileOwnerAdapter( + adapters: FileOwnerAdapters, + owner: FileOwner +): Adapter { + if ( + typeof owner.entityId !== 'string' || + !owner.entityId || + owner.entityId !== owner.entityId.trim() || + owner.entityId.length > 200 || + !Object.hasOwn(adapters, owner.entityType) + ) { + throw new OrchestrationError('not_found', 'File owner is unavailable') + } + const adapter = adapters[owner.entityType] + if (!adapter) throw new OrchestrationError('not_found', 'File owner is unavailable') + return adapter +} diff --git a/apps/sim/lib/workspace-files/ownership-query.ts b/apps/sim/lib/workspace-files/ownership-query.ts new file mode 100644 index 00000000000..b42c0287847 --- /dev/null +++ b/apps/sim/lib/workspace-files/ownership-query.ts @@ -0,0 +1,48 @@ +import { folder, workspaceFiles } from '@sim/db/schema' +import { and, eq, inArray, isNull, or } from 'drizzle-orm' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' + +/** Selects only coherent canonical ownership, including the audited workspace expansion fallback. */ +export function fileOwnerCondition(owner: EditableFileOwner, includeChatUploads = false) { + if (owner.entityType === 'project') { + return and( + eq(workspaceFiles.entityType, 'project'), + eq(workspaceFiles.entityId, owner.entityId), + eq(workspaceFiles.context, 'project'), + isNull(workspaceFiles.workspaceId), + isNull(workspaceFiles.organizationId), + isNull(workspaceFiles.chatId) + ) + } + return and( + eq(workspaceFiles.workspaceId, owner.entityId), + isNull(workspaceFiles.organizationId), + includeChatUploads + ? inArray(workspaceFiles.context, ['workspace', 'mothership']) + : eq(workspaceFiles.context, 'workspace'), + or( + and(isNull(workspaceFiles.entityType), isNull(workspaceFiles.entityId)), + and(eq(workspaceFiles.entityType, 'workspace'), eq(workspaceFiles.entityId, owner.entityId)) + ) + ) +} + +/** Other folder resource types remain workspace-only and are never selected as editable file folders. */ +export function fileFolderOwnerCondition(owner: EditableFileOwner) { + return and( + eq(folder.resourceType, 'file'), + owner.entityType === 'workspace' + ? and( + eq(folder.workspaceId, owner.entityId), + or( + and(isNull(folder.entityType), isNull(folder.entityId)), + and(eq(folder.entityType, 'workspace'), eq(folder.entityId, owner.entityId)) + ) + ) + : and( + isNull(folder.workspaceId), + eq(folder.entityType, 'project'), + eq(folder.entityId, owner.entityId) + ) + ) +} diff --git a/apps/sim/lib/workspace-files/ownership.test.ts b/apps/sim/lib/workspace-files/ownership.test.ts new file mode 100644 index 00000000000..2b0f642495e --- /dev/null +++ b/apps/sim/lib/workspace-files/ownership.test.ts @@ -0,0 +1,111 @@ +import { describe, expect, it } from 'vitest' +import { resolveFileOwner } from '@/lib/workspace-files/ownership' + +const legacyWorkspaceFile = { + entityType: null, + entityId: null, + context: 'workspace', + workspaceId: 'workspace-a', + organizationId: null, + userId: 'uploader-a', + chatId: null, + folderId: null, +} + +const projectFile = { + ...legacyWorkspaceFile, + entityType: 'project', + entityId: 'project-a', + context: 'project', + workspaceId: null, +} + +describe('file ownership isolation', () => { + it('keeps audited legacy workspace ownership while its pair awaits backfill', () => { + expect(resolveFileOwner(legacyWorkspaceFile)).toEqual({ + entityType: 'workspace', + entityId: 'workspace-a', + }) + }) + + it.each([ + { entityType: 'workspace', entityId: null }, + { entityType: null, entityId: 'workspace-a' }, + { entityType: 'workspace', entityId: '' }, + { entityType: 'unrecognized', entityId: 'workspace-a' }, + { entityType: 'workspace', entityId: 'workspace-b' }, + { entityType: 'user', entityId: 'uploader-a' }, + ])('rejects a conflicting or incomplete pair %j', (binding) => { + expect(resolveFileOwner({ ...legacyWorkspaceFile, ...binding })).toBeNull() + }) + + it('does not change shared ownership when the uploader changes', () => { + expect( + resolveFileOwner({ + ...legacyWorkspaceFile, + entityType: 'workspace', + entityId: 'workspace-a', + userId: 'different-uploader', + }) + ).toEqual({ entityType: 'workspace', entityId: 'workspace-a' }) + expect(resolveFileOwner({ ...projectFile, userId: 'different-uploader' })).toEqual({ + entityType: 'project', + entityId: 'project-a', + }) + }) + + it('rejects ambiguous legacy ownership instead of picking the first scope', () => { + expect( + resolveFileOwner({ ...legacyWorkspaceFile, organizationId: 'organization-a' }) + ).toBeNull() + expect(resolveFileOwner({ ...legacyWorkspaceFile, workspaceId: '' })).toBeNull() + }) + + it.each(['chat', 'general', 'logs', 'table-import', 'og-images', 'unrecognized'])( + 'does not treat an uploader as the owner of an unclassified %s file', + (context) => { + const file = { ...legacyWorkspaceFile, context, workspaceId: null } + expect(resolveFileOwner(file)).toBeNull() + expect(resolveFileOwner({ ...file, entityType: 'user', entityId: file.userId })).toBeNull() + } + ) + + it.each([ + { entityType: null, entityId: null }, + { entityType: 'workspace', entityId: 'workspace-a' }, + { entityType: 'project', entityId: '' }, + { workspaceId: 'workspace-a' }, + { organizationId: 'organization-a' }, + { chatId: 'chat-a' }, + { context: 'workspace' }, + ])('rejects Project ownership mixed with legacy associations %j', (binding) => { + expect(resolveFileOwner({ ...projectFile, ...binding })).toBeNull() + }) + + it('preserves organization KB ownership independently of the uploader', () => { + const file = { + ...legacyWorkspaceFile, + context: 'knowledge-base', + workspaceId: null, + organizationId: 'organization-a', + } + expect(resolveFileOwner(file)).toEqual({ + entityType: 'organization', + entityId: 'organization-a', + }) + expect( + resolveFileOwner({ ...file, entityType: 'organization', entityId: 'organization-b' }) + ).toBeNull() + }) + + it.each(['copilot', 'profile-pictures'])( + 'binds audited personal %s files to their existing personal owner', + (context) => { + const file = { ...legacyWorkspaceFile, context, workspaceId: null } + expect(resolveFileOwner(file)).toEqual({ entityType: 'user', entityId: 'uploader-a' }) + expect( + resolveFileOwner({ ...file, entityType: 'user', entityId: 'different-person' }) + ).toBeNull() + } + ) +}) diff --git a/apps/sim/lib/workspace-files/ownership.ts b/apps/sim/lib/workspace-files/ownership.ts new file mode 100644 index 00000000000..42a119a8c28 --- /dev/null +++ b/apps/sim/lib/workspace-files/ownership.ts @@ -0,0 +1,76 @@ +import type { ResourceEntityType } from '@sim/auth/principal' +import type { WorkspaceFileRow } from '@sim/db/schema' + +/** Persisted ownership is independent of creator attribution and caller authority. */ +export interface FileOwner { + entityType: ResourceEntityType + entityId: string +} + +/** Owners whose durable files support the shared Files editor and folder lifecycle. */ +export interface EditableFileOwner { + entityType: 'workspace' | 'project' + entityId: string +} + +type PersistedFileOwnership = Pick< + WorkspaceFileRow, + 'entityType' | 'entityId' | 'context' | 'workspaceId' | 'organizationId' | 'userId' | 'chatId' +> + +function resolveLegacyFileOwner(file: PersistedFileOwnership): FileOwner | null { + if (file.organizationId === null && file.workspaceId) { + switch (file.context) { + case 'workspace': + case 'mothership': + case 'execution': + case 'workspace-logos': + case 'knowledge-base': + return { entityType: 'workspace', entityId: file.workspaceId } + } + } + if (file.context === 'knowledge-base' && file.workspaceId === null && file.organizationId) { + return { entityType: 'organization', entityId: file.organizationId } + } + if ( + (file.context === 'copilot' || file.context === 'profile-pictures') && + file.workspaceId === null && + file.organizationId === null && + file.userId + ) { + return { entityType: 'user', entityId: file.userId } + } + return null +} + +/** + * Resolves the persisted pair without repairing conflicts or granting access. + * During expansion, an absent pair uses only the audited legacy mappings from + * `workspace_file_legacy_entity`; unclassified contexts remain unresolved. + */ +export function resolveFileOwner(file: PersistedFileOwnership): FileOwner | null { + if (file.entityType === null && file.entityId === null) return resolveLegacyFileOwner(file) + if (file.entityType === null || !file.entityId) return null + + if (file.entityType === 'project') { + if ( + file.context !== 'project' || + file.workspaceId !== null || + file.organizationId !== null || + file.chatId !== null + ) { + return null + } + return { entityType: 'project', entityId: file.entityId } + } + + const legacyOwner = resolveLegacyFileOwner(file) + if ( + !legacyOwner || + legacyOwner.entityType !== file.entityType || + legacyOwner.entityId !== file.entityId + ) { + return null + } + return legacyOwner +} diff --git a/apps/sim/lib/workspace-files/search/README.md b/apps/sim/lib/workspace-files/search/README.md index 6119642bec1..8b532f7f8d8 100644 --- a/apps/sim/lib/workspace-files/search/README.md +++ b/apps/sim/lib/workspace-files/search/README.md @@ -66,3 +66,11 @@ Set `FILE_SEARCH_BENCHMARK_FILES` to change the synthetic file count (default 1, ## PostgreSQL references The design uses PostgreSQL's documented [TOAST behavior](https://www.postgresql.org/docs/17/storage-toast.html), [trigram index support for LIKE and regex](https://www.postgresql.org/docs/17/pgtrgm.html), [GIN pending-list tradeoffs](https://www.postgresql.org/docs/17/gin.html#GIN-FAST-UPDATE), [index storage parameters](https://www.postgresql.org/docs/17/sql-createindex.html), and [EXPLAIN guidance](https://www.postgresql.org/docs/17/using-explain.html). The chunk size and query paths are application choices validated by the synthetic fixture, not PostgreSQL hard limits. + +### File owner expansion + +Migration `0401_file_search_owner_scope.sql` adds canonical workspace/Project ownership to the existing build, revision, and chunk pipeline. Legacy workspace inserts are translated by triggers, and the existing workspace trigram index remains usable. Project rows use a separate owner-scoped trigram index on the same chunk table. Organizations and personal users are not enabled search owners. + +The new `file_search_dispatch_queue` replaces the workspace-keyed scheduler because older workers cannot decode a Project without a workspace. During expansion, workspace mutations enqueue both queues, while the new dispatcher reads the owner queue. Both deployments share the dispatcher lock, revision claim token, build lease, and publication fence. Drain old app dispatchers and Trigger workers/retries before enabling Project dispatch. Retire the legacy queue and its dual enqueue in a later contract deploy; they are not a second indexing pipeline. The `file-search-owners-v3` cursor performs bounded owner/file keyset backfill and hourly reconciliation without rewriting extracted text in the migration. + +Generated document indexing never executes source. A cached artifact must match its owner and current input manifest. Each published build stores at most 500 input identities. Queries exclude a build when any input is changed, archived, deleted, or outside the owner, and report it as pending coverage. Input mutations wake their owner queue; the dispatcher retires stale manifests in bounded batches before rebuilding. Expired build cleanup also removes its dependency records. A cache miss indexes the stored generation source as text until an authorized rendering path creates a current artifact. diff --git a/apps/sim/lib/workspace-files/search/artifact-ready.ts b/apps/sim/lib/workspace-files/search/artifact-ready.ts new file mode 100644 index 00000000000..45a6c19c2c2 --- /dev/null +++ b/apps/sim/lib/workspace-files/search/artifact-ready.ts @@ -0,0 +1,95 @@ +import { + fileSearchDispatchQueue, + workspaceFileSearchBuild, + workspaceFileSearchRevision, +} from '@sim/db/schema' +import { and, eq, sql } from 'drizzle-orm' +import type { DbTransaction } from '@/lib/db/types' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' +import { lockFileSearchInputs } from '@/lib/workspace-files/search/index-state' +import { + fileSearchOwnerCondition, + fileSearchOwnerFields, + resolveFileSearchOwner, +} from '@/lib/workspace-files/search/scope' +import type { FileSearchDependencyIdentity } from '@/lib/workspace-files/search/source' + +/** A renderer's private receipt; surfaces cannot supply it through a file contract. */ +interface FileSearchArtifactCompletion { + owner: EditableFileOwner + file: { fileId: string; key: string; contentUpdatedAt: Date } + dependencies: readonly FileSearchDependencyIdentity[] + artifactKey: string +} + +/** Requeues only current stored inputs and fences workers that read before the artifact existed. */ +export async function markFileSearchArtifactReadyInTx( + tx: DbTransaction, + input: FileSearchArtifactCompletion +): Promise { + const owner = resolveFileSearchOwner(input) + const prefix = + owner.entityType === 'workspace' + ? `copilot-doc-compiled/${owner.entityId}/` + : `project/${owner.entityId}/compiled/` + if ( + !input.artifactKey.startsWith(prefix) || + !/^[a-f0-9]{64}\.(docx|pptx|pdf|xlsx)$/.test(input.artifactKey.slice(prefix.length)) + ) + throw new Error('Artifact receipt does not belong to this file owner') + const revision = { + owner, + fileId: input.file.fileId, + sourceContentUpdatedAt: input.file.contentUpdatedAt, + } + if (!(await lockFileSearchInputs(tx, revision, input.dependencies, input.file.key))) return false + const predicate = and( + eq(workspaceFileSearchRevision.fileId, revision.fileId), + fileSearchOwnerCondition(workspaceFileSearchRevision, owner), + eq(workspaceFileSearchRevision.sourceContentUpdatedAt, revision.sourceContentUpdatedAt) + ) + const [state] = await tx.select().from(workspaceFileSearchRevision).where(predicate).limit(1) + if (state?.buildId) { + const [build] = await tx + .select({ artifactKey: workspaceFileSearchBuild.artifactKey }) + .from(workspaceFileSearchBuild) + .where(eq(workspaceFileSearchBuild.id, state.buildId)) + .for('update') + if (state.status === 'ready' && build?.artifactKey === input.artifactKey) return false + await tx + .update(workspaceFileSearchBuild) + .set({ expiresAt: sql`clock_timestamp()` }) + .where(eq(workspaceFileSearchBuild.id, state.buildId)) + } + const now = new Date() + await tx + .insert(workspaceFileSearchRevision) + .values({ + ...fileSearchOwnerFields(owner), + fileId: revision.fileId, + sourceContentUpdatedAt: revision.sourceContentUpdatedAt, + }) + .onConflictDoUpdate({ + target: workspaceFileSearchRevision.fileId, + set: { + status: 'pending', + buildId: null, + dispatchedAt: null, + handoffExpiresAt: null, + failureReason: null, + lineCount: 0, + indexedBytes: 0, + chunkCount: 0, + updatedAt: now, + }, + setWhere: predicate, + }) + await tx + .insert(fileSearchDispatchQueue) + .values({ ...owner, enqueuedAt: now, updatedAt: now }) + .onConflictDoUpdate({ + target: [fileSearchDispatchQueue.entityType, fileSearchDispatchQueue.entityId], + set: { updatedAt: now }, + }) + return true +} diff --git a/apps/sim/lib/workspace-files/search/candidates.ts b/apps/sim/lib/workspace-files/search/candidates.ts index 8a218320055..2c10ab9f267 100644 --- a/apps/sim/lib/workspace-files/search/candidates.ts +++ b/apps/sim/lib/workspace-files/search/candidates.ts @@ -5,18 +5,24 @@ import { } from '@sim/db/schema' import { and, asc, eq, isNull, or, type SQL, sql } from 'drizzle-orm' import type { DbTransaction } from '@/lib/db/types' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' import { FILE_SEARCH_CANDIDATE_PAGE_SIZE, FILE_SEARCH_CANDIDATE_PROBE_SIZE, } from '@/lib/workspace-files/search/constants' import type { CompiledFileSearchPattern } from '@/lib/workspace-files/search/pattern' +import { + currentFileSearchDependencies, + fileSearchOwnerCondition, + searchableFileCondition, +} from '@/lib/workspace-files/search/scope' import { buildMatchExpression } from '@/lib/workspace-files/search/sql-pattern' export interface FileSearchCandidate { fileId: string fileName: string fileKey: string - ownerUserId: string + ownerUserId: string | null contentUpdatedAt: Date buildId: string ordinal: number @@ -25,7 +31,7 @@ export interface FileSearchCandidate { } interface CandidateScope { - workspaceId: string + owner: EditableFileOwner pattern: CompiledFileSearchPattern folderPredicate?: SQL } @@ -58,7 +64,7 @@ function candidatePredicate(pattern: CompiledFileSearchPattern): SQL { /** Bounded unordered probe avoids sorting all matching text for broad queries. */ export async function probeFileSearchCandidates( tx: DbTransaction, - { workspaceId, pattern, folderPredicate }: CandidateScope + { owner, pattern, folderPredicate }: CandidateScope ): Promise { const probe = tx .select({ @@ -77,7 +83,7 @@ export async function probeFileSearchCandidates( workspaceFileSearchRevision, and( eq(workspaceFileSearchRevision.buildId, workspaceFileSearchChunk.buildId), - eq(workspaceFileSearchRevision.workspaceId, workspaceId), + fileSearchOwnerCondition(workspaceFileSearchRevision, owner), eq(workspaceFileSearchRevision.status, 'ready') ) ) @@ -90,9 +96,9 @@ export async function probeFileSearchCandidates( ) .where( and( - eq(workspaceFileSearchChunk.workspaceId, workspaceId), - eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), + fileSearchOwnerCondition(workspaceFileSearchChunk, owner), + searchableFileCondition(owner), + currentFileSearchDependencies(workspaceFileSearchRevision.buildId, owner), isNull(workspaceFiles.deletedAt), folderPredicate, candidatePredicate(pattern) @@ -100,16 +106,17 @@ export async function probeFileSearchCandidates( ) .limit(FILE_SEARCH_CANDIDATE_PROBE_SIZE + 1) .as('probe') - return tx + const rows = await tx .select() .from(probe) .orderBy(asc(probe.fileName), asc(probe.fileId), asc(probe.lineStart), asc(probe.ordinal)) + return rows } /** A parameterized build scan preserves file order and can stop after one candidate page. */ export async function readOrderedFileSearchCandidates( tx: DbTransaction, - { workspaceId, pattern, folderPredicate }: CandidateScope, + { owner, pattern, folderPredicate }: CandidateScope, after?: CandidateCursor ): Promise { /** OFFSET 0 preserves the ordered file input instead of flattening into a global text scan. */ @@ -127,15 +134,15 @@ export async function readOrderedFileSearchCandidates( workspaceFileSearchRevision, and( eq(workspaceFileSearchRevision.fileId, workspaceFiles.id), - eq(workspaceFileSearchRevision.workspaceId, workspaceId), + fileSearchOwnerCondition(workspaceFileSearchRevision, owner), eq(workspaceFileSearchRevision.sourceContentUpdatedAt, workspaceFiles.contentUpdatedAt), eq(workspaceFileSearchRevision.status, 'ready') ) ) .where( and( - eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), + searchableFileCondition(owner), + currentFileSearchDependencies(workspaceFileSearchRevision.buildId, owner), isNull(workspaceFiles.deletedAt), folderPredicate, after @@ -157,7 +164,7 @@ export async function readOrderedFileSearchCandidates( .where( and( eq(workspaceFileSearchChunk.buildId, files.buildId), - eq(workspaceFileSearchChunk.workspaceId, workspaceId), + fileSearchOwnerCondition(workspaceFileSearchChunk, owner), candidatePredicate(pattern), after ? sql`(${files.fileName}, ${files.fileId}, ${workspaceFileSearchChunk.lineStart}) > (${after.name}, ${after.id}, ${after.lineStart})` @@ -167,7 +174,7 @@ export async function readOrderedFileSearchCandidates( .orderBy(asc(workspaceFileSearchChunk.lineStart), asc(workspaceFileSearchChunk.ordinal)) .limit(FILE_SEARCH_CANDIDATE_PAGE_SIZE) .as('chunks') - return tx + const rows = await tx .select({ fileId: files.fileId, fileName: files.fileName, @@ -183,4 +190,5 @@ export async function readOrderedFileSearchCandidates( .innerJoinLateral(chunks, sql`true`) .orderBy(asc(files.fileName), asc(files.fileId), asc(chunks.lineStart), asc(chunks.ordinal)) .limit(FILE_SEARCH_CANDIDATE_PAGE_SIZE) + return rows } diff --git a/apps/sim/lib/workspace-files/search/chunks.integration.ts b/apps/sim/lib/workspace-files/search/chunks.integration.ts index dc4887136f9..cf961f536ff 100644 --- a/apps/sim/lib/workspace-files/search/chunks.integration.ts +++ b/apps/sim/lib/workspace-files/search/chunks.integration.ts @@ -28,7 +28,7 @@ vi.mock('@/lib/uploads/contexts/workspace', () => ({ getWorkspaceFile: vi.fn(), fetchWorkspaceFileBuffer: vi.fn(), })) -vi.mock('@/lib/mothership/tools/server/files/doc-compile', () => ({ resolveServableDoc: vi.fn() })) +vi.mock('@/lib/uploads/documents/compile', () => ({ resolveServableDoc: vi.fn() })) vi.mock('@/lib/file-parsers', () => ({ parseBuffer: vi.fn(), isSupportedFileType: vi.fn() })) import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' @@ -126,7 +126,7 @@ describe('chunked workspace file search on PostgreSQL', () => { await connection`INSERT INTO workspace (id) VALUES (${workspaceId}) ON CONFLICT DO NOTHING` await connection`INSERT INTO workspace_files (id, workspace_id, context, content_updated_at, original_name, folder_id) VALUES (${fileId}, ${workspaceId}, 'workspace', ${revision.sourceContentUpdatedAt.toISOString()}::timestamp, ${name}, ${folder})` - return { ...revision, fileId, workspaceId } + return { sourceContentUpdatedAt: revision.sourceContentUpdatedAt, fileId, workspaceId } } async function index(text: string, target = revision) { const build = await beginFileSearchBuild(target) @@ -166,13 +166,14 @@ describe('chunked workspace file search on PostgreSQL', () => { await connection`CREATE TABLE workspace (id text PRIMARY KEY)` await connection`CREATE TABLE workspace_files (id text PRIMARY KEY, workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, context text NOT NULL, content_updated_at timestamp NOT NULL, deleted_at timestamp, - original_name text NOT NULL, key text NOT NULL DEFAULT 'key', user_id text NOT NULL DEFAULT 'owner', folder_id text)` + original_name text NOT NULL, key text NOT NULL DEFAULT 'key', user_id text NOT NULL DEFAULT 'owner', folder_id text, entity_type text, entity_id text, organization_id text, chat_id text)` for (const migration of [ '0313_puzzling_zodiak.sql', '0358_workspace_file_content_version_precision.sql', '0359_workspace_file_search_chunks.sql', ginWriteMigration, '0382_workspace_file_search_dispatch_handoff.sql', + '0401_file_search_owner_scope.sql', ]) { await applyMigration(migration) } @@ -194,8 +195,8 @@ describe('chunked workspace file search on PostgreSQL', () => { }) beforeEach(async () => { await connection`TRUNCATE workspace, workspace_files, workspace_file_search_revision, workspace_file_search_build, - workspace_file_search_chunk, workspace_file_search_index, workspace_file_search_segment, workspace_file_search_dispatch_queue, workspace_file_search_backfill` - await connection`INSERT INTO workspace_file_search_backfill (id, completed_at) VALUES ('workspace-file-search-chunks-v2', now())` + workspace_file_search_chunk, workspace_file_search_index, workspace_file_search_segment, workspace_file_search_dispatch_queue, workspace_file_search_backfill, file_search_dependency, file_search_dispatch_queue` + await connection`INSERT INTO workspace_file_search_backfill (id, completed_at) VALUES ('file-search-owners-v3', now())` await addFile('file-1') }) afterAll(async () => { diff --git a/apps/sim/lib/workspace-files/search/constants.ts b/apps/sim/lib/workspace-files/search/constants.ts index d715e44eabf..2c846b22b8d 100644 --- a/apps/sim/lib/workspace-files/search/constants.ts +++ b/apps/sim/lib/workspace-files/search/constants.ts @@ -112,3 +112,5 @@ export const FILE_SEARCH_INDEX_MAX_DURATION_SECONDS = 15 * 60 export const FILE_SEARCH_INDEX_STALE_DISPATCH_MS = 6 * 60 * 60 * 1000 export const FILE_SEARCH_INDEX_STALE_REAP_LIMIT = 100 export const FILE_SEARCH_BACKFILL_PAGE_SIZE = 1000 + +export const FILE_SEARCH_MAX_DEPENDENCIES = 500 diff --git a/apps/sim/lib/workspace-files/search/delivery.ts b/apps/sim/lib/workspace-files/search/delivery.ts new file mode 100644 index 00000000000..fbb009674c9 --- /dev/null +++ b/apps/sim/lib/workspace-files/search/delivery.ts @@ -0,0 +1,150 @@ +import type { WorkspaceFileRow } from '@sim/db/schema' +import { workspaceFileSearchRevision, workspaceFiles } from '@sim/db/schema' +import { compareStrings } from '@sim/utils/string' +import { and, asc, inArray, isNull } from 'drizzle-orm' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { DbTransaction } from '@/lib/db/types' +import type { FolderIdScope } from '@/lib/folders/scope' +import type { listFileFolders } from '@/lib/uploads/contexts/workspace' +import { + getBoundWorkspaceFileSecretProvenanceByMetadata, + mergeWorkspaceFileSecretProvenance, + type WorkspaceFileSecretProvenance, +} from '@/lib/uploads/contexts/workspace/workspace-file-secret-provenance' +import { toWorkspaceFileFolderPathView } from '@/lib/workspace-files/folder-display-path' +import { resolveFolderIdsForPaths } from '@/lib/workspace-files/folder-path-selection' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' +import type { FileSearchResult } from '@/lib/workspace-files/search/repository' +import { searchableFileCondition } from '@/lib/workspace-files/search/scope' +import { configureFileSearchTransaction } from '@/lib/workspace-files/search/transaction' + +export function resolveFileSearchFolderScope( + folders: Awaited>, + input: { folderPaths?: readonly string[]; includeSubfolders?: boolean } +): FolderIdScope | undefined { + if (input.folderPaths === undefined) return undefined + const selection = resolveFolderIdsForPaths( + folders.map((folder) => ({ + ...toWorkspaceFileFolderPathView(folder), + id: folder.id, + parentId: folder.parentId, + })), + input.folderPaths, + { includeSubfolders: input.includeSubfolders } + ) + if (selection.missingPath !== undefined) + throw new OrchestrationError('not_found', `Folder not found: ${selection.missingPath}`) + return { folderIds: selection.folderIds, includeRootItems: selection.includeRootItems } +} + +/** Validates a private index snapshot against current rows after its caller authorizes the owner. */ +export async function loadFileSearchDelivery( + tx: DbTransaction, + { + owner, + prepared, + scope, + signal, + }: { + owner: EditableFileOwner + prepared: FileSearchResult + scope?: FolderIdScope + signal?: AbortSignal + } +): Promise { + signal?.throwIfAborted() + const ids = [ + ...new Set( + prepared.sources.flatMap((source) => [ + source.identity.fileId, + ...source.dependencies.map((dependency) => dependency.fileId), + ]) + ), + ] + ids.sort(compareStrings) + await configureFileSearchTransaction(tx) + const rows: Pick< + WorkspaceFileRow, + 'id' | 'key' | 'context' | 'contentUpdatedAt' | 'secretProvenanceVersion' | 'folderId' + >[] = [] + for (let offset = 0; offset < ids.length; offset += 1_000) { + signal?.throwIfAborted() + rows.push( + ...(await tx + .select({ + id: workspaceFiles.id, + key: workspaceFiles.key, + context: workspaceFiles.context, + contentUpdatedAt: workspaceFiles.contentUpdatedAt, + secretProvenanceVersion: workspaceFiles.secretProvenanceVersion, + folderId: workspaceFiles.folderId, + }) + .from(workspaceFiles) + .where( + and( + searchableFileCondition(owner), + inArray(workspaceFiles.id, ids.slice(offset, offset + 1_000)), + isNull(workspaceFiles.deletedAt) + ) + ) + .orderBy(asc(workspaceFiles.id)) + .for('share')) + ) + } + const byId = new Map(rows.map((row) => [row.id, row])) + const states = + prepared.sources.length === 0 + ? [] + : await tx + .select({ + fileId: workspaceFileSearchRevision.fileId, + buildId: workspaceFileSearchRevision.buildId, + status: workspaceFileSearchRevision.status, + }) + .from(workspaceFileSearchRevision) + .where( + inArray( + workspaceFileSearchRevision.fileId, + prepared.sources.map((source) => source.identity.fileId) + ) + ) + const statesById = new Map(states.map((state) => [state.fileId, state])) + for (const source of prepared.sources) { + const current = byId.get(source.identity.fileId) + const state = statesById.get(source.identity.fileId) + if ( + !current || + current.key !== source.identity.key || + current.contentUpdatedAt.getTime() !== source.identity.contentUpdatedAt?.getTime() || + state?.status !== 'ready' || + state.buildId !== source.buildId || + (scope && + (current.folderId ? !scope.folderIds.has(current.folderId) : !scope.includeRootItems)) + ) + throw new OrchestrationError( + 'conflict', + 'A matching file changed during search. Retry the search.' + ) + for (const dependency of source.dependencies) { + const currentInput = byId.get(dependency.fileId) + if ( + !currentInput || + currentInput.key !== dependency.key || + currentInput.contentUpdatedAt.getTime() !== dependency.sourceContentUpdatedAt.getTime() + ) + throw new OrchestrationError( + 'conflict', + 'A matching document input changed during search. Retry the search.' + ) + } + } + const provenance = await getBoundWorkspaceFileSecretProvenanceByMetadata(tx, rows) + let secretProvenance: WorkspaceFileSecretProvenance = { status: 'exact', entries: [] } + for (const row of rows) + secretProvenance = mergeWorkspaceFileSecretProvenance( + secretProvenance, + provenance.get(row.id) ?? { status: 'unknown' } + ) + signal?.throwIfAborted() + return secretProvenance +} diff --git a/apps/sim/lib/workspace-files/search/dispatcher.integration.ts b/apps/sim/lib/workspace-files/search/dispatcher.integration.ts index e32b13aaccb..5bbb4d44162 100644 --- a/apps/sim/lib/workspace-files/search/dispatcher.integration.ts +++ b/apps/sim/lib/workspace-files/search/dispatcher.integration.ts @@ -1,4 +1,6 @@ /** Real PostgreSQL cancellation must roll back preparation and release its advisory lock. */ +import { readFileSync } from 'node:fs' +import { resolve } from 'node:path' import { withUtcTimestamps } from '@sim/db/timestamps' import { getPostgresErrorCode } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' @@ -47,7 +49,7 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { max: 3, prepare: false, fetch_types: false, - connection: { search_path: schemaName }, + connection: { search_path: `${schemaName},public` }, onnotice: () => {}, debug: (_connection: unknown, query: string, params: readonly unknown[]) => { statements.push({ query, params }) @@ -69,7 +71,8 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { */ await connection`CREATE TABLE workspace_files ( id text PRIMARY KEY, workspace_id text, context text NOT NULL, - deleted_at timestamp, content_updated_at timestamp NOT NULL + deleted_at timestamp, content_updated_at timestamp NOT NULL, + entity_type text, entity_id text, key text NOT NULL DEFAULT 'key' )` await connection`CREATE TABLE workspace_file_search_revision ( file_id text PRIMARY KEY, workspace_id text NOT NULL, @@ -93,19 +96,34 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { await connection`CREATE INDEX ON workspace_file_search_revision (workspace_id, dispatched_at) WHERE status = 'pending' AND dispatched_at IS NOT NULL` await connection`INSERT INTO workspace_file_search_backfill (id, updated_at) - VALUES ('workspace-file-search-chunks-v2', '2026-09-16 00:00:00')` - await connection`CREATE TABLE workspace_file_search_build (id text PRIMARY KEY, expires_at timestamp)` - await connection`CREATE TABLE workspace_file_search_chunk (build_id text NOT NULL, ordinal integer NOT NULL, PRIMARY KEY(build_id, ordinal))` + VALUES ('file-search-owners-v3', '2026-09-16 00:00:00')` + await connection`CREATE TABLE workspace_file_search_build (id text PRIMARY KEY, workspace_id text NOT NULL, file_id text NOT NULL, expires_at timestamp)` + await connection`CREATE TABLE workspace_file_search_chunk (build_id text NOT NULL, workspace_id text NOT NULL, content text NOT NULL, ordinal integer NOT NULL, PRIMARY KEY(build_id, ordinal))` + const migration = readFileSync( + resolve(process.cwd(), '../../packages/db/migrations/0401_file_search_owner_scope.sql'), + 'utf8' + ).replaceAll('"public".', `"${schemaName}".`) + const session = await connection.reserve() + try { + await session`BEGIN` + for (const statement of migration.split('--> statement-breakpoint')) + if (statement.trim()) await session.unsafe(statement) + await session`COMMIT` + } finally { + await session`ROLLBACK` + await session`RESET lock_timeout` + session.release() + } database.current = drizzle(connection) }) beforeEach(async () => { mocks.batchTrigger.mockReset() await connection`DROP TRIGGER IF EXISTS slow_backfill ON workspace_file_search_backfill` - await connection`TRUNCATE workspace_files, workspace_file_search_revision, workspace_file_search_dispatch_queue` + await connection`TRUNCATE workspace_files, workspace_file_search_revision, workspace_file_search_dispatch_queue, file_search_dispatch_queue` await connection`UPDATE workspace_file_search_backfill SET updated_at = '2026-09-16 00:00:00', completed_at = NULL, - after_workspace_id = NULL, after_file_id = NULL` + after_workspace_id = NULL, after_entity_type = NULL, after_entity_id = NULL, after_file_id = NULL` }) afterAll(async () => { @@ -126,20 +144,69 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { }) } - async function seedQueue(workspaceId: string, queued: number, active = 0) { + async function seedQueue( + workspaceId: string, + queued: number, + active = 0, + entityType: 'workspace' | 'project' = 'workspace' + ) { await connection`UPDATE workspace_file_search_backfill SET completed_at = now()` - await connection`INSERT INTO workspace_files (id, workspace_id, context, content_updated_at) - SELECT ${workspaceId} || '-' || lpad(n::text, 6, '0'), ${workspaceId}, 'workspace', '2026-09-16' + await connection`INSERT INTO workspace_files (id, workspace_id, context, entity_type, entity_id, content_updated_at) + SELECT ${entityType === 'workspace' ? '' : 'project-'} || ${workspaceId} || '-' || lpad(n::text, 6, '0'), + ${entityType === 'workspace' ? workspaceId : null}, ${entityType}, ${entityType}, ${workspaceId}, '2026-09-16' FROM generate_series(1, ${queued + active}) n` await connection`INSERT INTO workspace_file_search_revision - (file_id, workspace_id, source_content_updated_at, status, updated_at, dispatched_at) - SELECT id, workspace_id, content_updated_at, 'pending', '2026-09-16', + (file_id, workspace_id, entity_type, entity_id, source_content_updated_at, status, updated_at, dispatched_at) + SELECT id, workspace_id, entity_type, entity_id, content_updated_at, 'pending', '2026-09-16', CASE WHEN row_number() OVER (ORDER BY id DESC) <= ${active} THEN now() ELSE NULL END - FROM workspace_files WHERE workspace_id = ${workspaceId}` - await connection`INSERT INTO workspace_file_search_dispatch_queue - (workspace_id, enqueued_at, updated_at) VALUES (${workspaceId}, now(), now())` + FROM workspace_files WHERE entity_type = ${entityType} AND entity_id = ${workspaceId}` + await connection`INSERT INTO file_search_dispatch_queue + (entity_type, entity_id, enqueued_at, updated_at) VALUES (${entityType}, ${workspaceId}, now(), now())` } + it('leaves a busy Project queued without blocking unrelated workspace dispatch', async () => { + await seedQueue('busy-project', 1, 0, 'project') + await seedQueue('free-workspace', 1) + const holder = await connection.reserve() + try { + await holder`BEGIN` + await holder`SELECT pg_advisory_xact_lock(hashtextextended('project:busy-project', 0))` + const result = await prepareWorkspaceFileSearchDispatch() + expect(result.payloads).toHaveLength(1) + expect(result.payloads[0].owner).toEqual({ + entityType: 'workspace', + entityId: 'free-workspace', + }) + const [waiting] = await connection`SELECT count(*)::int AS count + FROM file_search_dispatch_queue WHERE entity_type = 'project' AND entity_id = 'busy-project'` + expect(waiting.count).toBe(1) + await holder`COMMIT` + const resumed = await prepareWorkspaceFileSearchDispatch() + expect(resumed.payloads).toHaveLength(1) + expect(resumed.payloads[0].owner).toEqual({ entityType: 'project', entityId: 'busy-project' }) + } finally { + await holder`ROLLBACK` + holder.release() + } + }) + + it('separates owner capacity when a workspace and Project use the same identifier', async () => { + await seedQueue('shared-id', 2, 1) + await seedQueue('shared-id', 2, 0, 'project') + const result = await prepareWorkspaceFileSearchDispatch() + expect( + result.payloads.filter((payload) => payload.owner?.entityType === 'workspace') + ).toHaveLength(1) + expect( + result.payloads.filter((payload) => payload.owner?.entityType === 'project') + ).toHaveLength(2) + expect(result.payloads.every((payload) => payload.owner?.entityId === 'shared-id')).toBe(true) + const [invalid] = + await connection`SELECT count(*)::int AS count FROM workspace_file_search_revision + WHERE entity_type = 'project' AND workspace_id IS NOT NULL` + expect(invalid.count).toBe(0) + }) + it('skips a locked candidate without losing it or exceeding workspace capacity', async () => { await seedQueue('workspace-1', 3, 1) await connection.begin(async (tx) => { @@ -242,7 +309,9 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { statement.query.includes('for share of "workspace_files"') ) expect(walk).toBeDefined() - expect(walk?.query).toContain('"workspace_files"."workspace_id", "workspace_files"."id") >') + expect(walk?.query).toContain( + 'coalesce("workspace_files"."entity_id", "workspace_files"."workspace_id"), "workspace_files"."id") >' + ) const plan = await connection.begin(async (tx) => { /** @@ -257,7 +326,7 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { return rows.map((row: Record) => row['QUERY PLAN']).join('\n') }) - expect(plan).toContain('workspace_files_workspace_active_keyset_idx') + expect(plan).toContain('workspace_files_search_owner_keyset_idx') expect(plan).toMatch(/Index Cond:.*ROW\(/) }, 30_000) @@ -289,7 +358,7 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { /** The locked candidate scan must be fed by the ordered index walk, not a sorted hash join. */ expect(plan).toMatch( - /LockRows[^\n]*\n\s*-> {2}Nested Loop[^\n]*\n\s*-> {2}Index Scan using workspace_file_search_revision_pending_idx/ + /LockRows[^\n]*\n\s*-> {2}Nested Loop[^\n]*\n\s*-> {2}Index Scan using file_search_revision_owner_pending_idx/ ) expect(plan).not.toMatch(/Sort Key: search_index(_\d+)?\.updated_at/) }, 30_000) @@ -497,8 +566,8 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { await connection`INSERT INTO workspace_file_search_revision (file_id, workspace_id, source_content_updated_at, status, updated_at) VALUES (${fileId}, ${workspaceId}, '2026-09-16', 'pending', now())` - await connection`INSERT INTO workspace_file_search_dispatch_queue - (workspace_id, enqueued_at, updated_at) VALUES (${workspaceId}, now(), now())` + await connection`INSERT INTO file_search_dispatch_queue + (entity_type, entity_id, enqueued_at, updated_at) VALUES ('workspace', ${workspaceId}, now(), now())` await connection`CREATE TABLE cleanup_timeouts ( lock_timeout text, statement_timeout text, transaction_timeout text )` @@ -526,7 +595,7 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { payload: { dispatchToken: expect.any(String), fileId, - workspaceId, + owner: { entityType: 'workspace', entityId: workspaceId }, sourceContentUpdatedAt: '2026-09-16T00:00:00.000Z', }, }), @@ -536,9 +605,9 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { WHERE file_id = ${fileId}` expect(index.dispatched_at).toBeNull() expect(index.handoff_expires_at).toBeNull() - const [queued] = await connection`SELECT workspace_id FROM workspace_file_search_dispatch_queue - WHERE workspace_id = ${workspaceId}` - expect(queued.workspace_id).toBe(workspaceId) + const [queued] = await connection`SELECT entity_id FROM file_search_dispatch_queue + WHERE entity_type = 'workspace' AND entity_id = ${workspaceId}` + expect(queued.entity_id).toBe(workspaceId) const timeouts = await connection`SELECT * FROM cleanup_timeouts` expect([...timeouts]).toEqual([ { lock_timeout: '0', statement_timeout: '0', transaction_timeout: '0' }, diff --git a/apps/sim/lib/workspace-files/search/dispatcher.test.ts b/apps/sim/lib/workspace-files/search/dispatcher.test.ts index b6c0342ae96..d76f0de626d 100644 --- a/apps/sim/lib/workspace-files/search/dispatcher.test.ts +++ b/apps/sim/lib/workspace-files/search/dispatcher.test.ts @@ -1,6 +1,6 @@ import { + fileSearchDispatchQueue, workspaceFileSearchBackfill, - workspaceFileSearchDispatchQueue, workspaceFileSearchRevision, } from '@sim/db/schema' import { dbChainMock, dbChainMockFns, queueTableRows, resetDbChainMock } from '@sim/testing' @@ -59,7 +59,7 @@ describe('workspace file search dispatch policy', () => { options: { idempotencyKey: 'workspace-file-search-v2:file-1:2026-08-29T12:00:00.000Z:initial', idempotencyKeyTTL: '1h', - tags: ['workspaceId:workspace-1', 'fileId:file-1'], + tags: ['fileOwner:workspace:workspace-1', 'fileId:file-1'], region: 'us-east-1', }, }, @@ -141,14 +141,17 @@ describe('workspace file search dispatch deadlines', () => { async (releaseFails) => { queueTableRows(workspaceFileSearchBackfill, [{ completedAt: new Date() }]) queueTableRows(workspaceFileSearchRevision, []) - queueTableRows(workspaceFileSearchDispatchQueue, [{ workspaceId: 'workspace-1' }]) + queueTableRows(fileSearchDispatchQueue, [ + { entityType: 'workspace', entityId: 'workspace-1' }, + ]) dbChainMockFns.execute .mockResolvedValueOnce([]) .mockResolvedValueOnce([{ acquired: true }]) .mockResolvedValueOnce([{ active: 0 }]) .mockResolvedValueOnce([ { - workspaceId: 'workspace-1', + entityType: 'workspace', + entityId: 'workspace-1', fileId: 'file-1', sourceContentUpdatedAt: new Date('2026-09-16T00:00:00Z'), }, @@ -194,14 +197,16 @@ describe('workspace file search dispatch deadlines', () => { queueTableRows(workspaceFileSearchBackfill, [{ completedAt: new Date() }]) queueTableRows(workspaceFileSearchRevision, [ { - workspaceId: 'workspace-1', + entityType: 'workspace', + entityId: 'workspace-1', fileId: 'file-1', sourceContentUpdatedAt: new Date('2026-09-16T00:00:00Z'), currentFileId: 'file-1', handoffExpired: true, }, { - workspaceId: 'workspace-1', + entityType: 'workspace', + entityId: 'workspace-1', fileId: 'file-2', sourceContentUpdatedAt: new Date('2026-09-16T00:00:00Z'), currentFileId: 'file-2', @@ -233,14 +238,17 @@ describe('workspace file search dispatch deadlines', () => { async (handoffFails) => { queueTableRows(workspaceFileSearchBackfill, [{ completedAt: new Date() }]) queueTableRows(workspaceFileSearchRevision, []) - queueTableRows(workspaceFileSearchDispatchQueue, [{ workspaceId: 'workspace-1' }]) + queueTableRows(fileSearchDispatchQueue, [ + { entityType: 'workspace', entityId: 'workspace-1' }, + ]) dbChainMockFns.execute .mockResolvedValueOnce([]) .mockResolvedValueOnce([{ acquired: true }]) .mockResolvedValueOnce([{ active: 0 }]) .mockResolvedValueOnce([ { - workspaceId: 'workspace-1', + entityType: 'workspace', + entityId: 'workspace-1', fileId: 'file-1', sourceContentUpdatedAt: new Date('2026-09-16T00:00:00Z'), }, diff --git a/apps/sim/lib/workspace-files/search/dispatcher.ts b/apps/sim/lib/workspace-files/search/dispatcher.ts index 377380f0ed0..becbb76d505 100644 --- a/apps/sim/lib/workspace-files/search/dispatcher.ts +++ b/apps/sim/lib/workspace-files/search/dispatcher.ts @@ -1,7 +1,8 @@ import { db } from '@sim/db' import { + fileSearchDispatchQueue, workspaceFileSearchBackfill, - workspaceFileSearchDispatchQueue, + workspaceFileSearchBuild, workspaceFileSearchRevision, workspaceFiles, } from '@sim/db/schema' @@ -32,6 +33,7 @@ import { isInsideTriggerRun } from '@/lib/core/config/trigger-runtime' import { runDetached } from '@/lib/core/utils/background' import { tryAcquireAdvisoryXactLock } from '@/lib/db/advisory-locks' import type { DbTransaction } from '@/lib/db/types' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' import { FILE_SEARCH_BACKFILL_PAGE_SIZE, FILE_SEARCH_CLEANUP_BACKLOG_ROWS, @@ -54,12 +56,18 @@ import { markWorkspaceFileSearchIndexFailed, type WorkspaceFileSearchIndexPayload, } from '@/lib/workspace-files/search/indexing' +import { lockFileSearchOwners } from '@/lib/workspace-files/search/owner-policy' +import { + currentFileSearchDependencies, + fileSearchOwnerFields, + resolveFileSearchOwner, +} from '@/lib/workspace-files/search/scope' import { configureFileSearchTransaction } from '@/lib/workspace-files/search/transaction' import type { workspaceFileSearchIndexTask } from '@/background/workspace-file-search-index' const logger = createLogger('WorkspaceFileSearchDispatcher') const DISPATCH_LOCK_NAME = 'workspace-file-search-dispatch' -const BACKFILL_CURSOR_ID = 'workspace-file-search-chunks-v2' +const BACKFILL_CURSOR_ID = 'file-search-owners-v3' async function runDispatchPhase(phase: string, operation: () => Promise): Promise { const startedAt = Date.now() @@ -124,7 +132,10 @@ export function buildWorkspaceFileSearchTriggerItems( options: { idempotencyKey: `workspace-file-search-v2:${payload.fileId}:${payload.sourceContentUpdatedAt}:${payload.dispatchToken ?? 'initial'}`, idempotencyKeyTTL: '1h' as const, - tags: [`workspaceId:${payload.workspaceId}`, `fileId:${payload.fileId}`], + tags: [ + `fileOwner:${resolveFileSearchOwner(payload).entityType}:${resolveFileSearchOwner(payload).entityId}`, + `fileId:${payload.fileId}`, + ], region, }, })) @@ -144,24 +155,22 @@ function revisionFilter(rows: readonly RevisionIdentity[]): SQL | undefined { ) } -async function enqueueWorkspaces( +function rowOwner(row: { entityType: string; entityId: string }): EditableFileOwner { + return resolveFileSearchOwner({ owner: row }) +} + +async function enqueueOwners( tx: DbTransaction, - workspaceIds: readonly string[], + owners: readonly EditableFileOwner[], now: Date ): Promise { - const uniqueWorkspaceIds = [...new Set(workspaceIds)] - if (uniqueWorkspaceIds.length === 0) return + const unique = [...new Map(owners.map((owner) => [JSON.stringify(owner), owner])).values()] + if (unique.length === 0) return await tx - .insert(workspaceFileSearchDispatchQueue) - .values( - uniqueWorkspaceIds.map((workspaceId) => ({ - workspaceId, - enqueuedAt: now, - updatedAt: now, - })) - ) + .insert(fileSearchDispatchQueue) + .values(unique.map((owner) => ({ ...owner, enqueuedAt: now, updatedAt: now }))) .onConflictDoUpdate({ - target: workspaceFileSearchDispatchQueue.workspaceId, + target: [fileSearchDispatchQueue.entityType, fileSearchDispatchQueue.entityId], set: { updatedAt: now }, }) } @@ -198,39 +207,42 @@ async function seedBackfillPage(tx: DbTransaction, now: Date): Promise { now.getTime() - cursor.completedAt.getTime() < FILE_SEARCH_RECONCILE_INTERVAL_MS) ) return 0 - const afterWorkspaceId = cursor.completedAt ? null : cursor.afterWorkspaceId + const afterEntityType = cursor.completedAt ? null : cursor.afterEntityType + const afterEntityId = cursor.completedAt ? null : cursor.afterEntityId const afterFileId = cursor.completedAt ? null : cursor.afterFileId const rows = await tx .select({ - workspaceId: workspaceFiles.workspaceId, + entityType: sql`coalesce(${workspaceFiles.entityType}, 'workspace')`, + entityId: sql`coalesce(${workspaceFiles.entityId}, ${workspaceFiles.workspaceId})`, fileId: workspaceFiles.id, sourceContentUpdatedAt: workspaceFiles.contentUpdatedAt, }) .from(workspaceFiles) .where( and( - eq(workspaceFiles.context, 'workspace'), + sql`((${workspaceFiles.context} = 'workspace' AND ${workspaceFiles.workspaceId} IS NOT NULL) OR (${workspaceFiles.context} = 'project' AND ${workspaceFiles.entityType} = 'project' AND ${workspaceFiles.entityId} IS NOT NULL))`, isNull(workspaceFiles.deletedAt), - isNotNull(workspaceFiles.workspaceId), - afterWorkspaceId && afterFileId - ? sql`(${workspaceFiles.workspaceId}, ${workspaceFiles.id}) > (${afterWorkspaceId}, ${afterFileId})` + afterEntityType && afterEntityId && afterFileId + ? sql`(coalesce(${workspaceFiles.entityType}, 'workspace'), coalesce(${workspaceFiles.entityId}, ${workspaceFiles.workspaceId}), ${workspaceFiles.id}) > (${afterEntityType}, ${afterEntityId}, ${afterFileId})` : undefined ) ) - .orderBy(asc(workspaceFiles.workspaceId), asc(workspaceFiles.id)) + .orderBy( + sql`coalesce(${workspaceFiles.entityType}, 'workspace')`, + sql`coalesce(${workspaceFiles.entityId}, ${workspaceFiles.workspaceId})`, + asc(workspaceFiles.id) + ) .limit(FILE_SEARCH_BACKFILL_PAGE_SIZE) .for('share', { of: workspaceFiles }) - const files = rows.filter( - (row): row is typeof row & { workspaceId: string } => row.workspaceId !== null - ) + const files = rows.map((row) => ({ ...row, owner: rowOwner(row) })) if (files.length > 0) { await tx .insert(workspaceFileSearchRevision) .values( files.map((file) => ({ - workspaceId: file.workspaceId, + ...fileSearchOwnerFields(file.owner), fileId: file.fileId, sourceContentUpdatedAt: file.sourceContentUpdatedAt, status: 'pending' as const, @@ -238,9 +250,9 @@ async function seedBackfillPage(tx: DbTransaction, now: Date): Promise { })) ) .onConflictDoNothing() - await enqueueWorkspaces( + await enqueueOwners( tx, - files.map((file) => file.workspaceId), + files.map((file) => file.owner), now ) } @@ -249,7 +261,8 @@ async function seedBackfillPage(tx: DbTransaction, now: Date): Promise { await tx .update(workspaceFileSearchBackfill) .set({ - afterWorkspaceId: last?.workspaceId ?? afterWorkspaceId, + afterEntityType: last?.entityType ?? afterEntityType, + afterEntityId: last?.entityId ?? afterEntityId, afterFileId: last?.fileId ?? afterFileId, completedAt: rows.length < FILE_SEARCH_BACKFILL_PAGE_SIZE ? now : null, updatedAt: now, @@ -274,7 +287,8 @@ async function reapStaleClaims( const staleBefore = new Date(now.getTime() - FILE_SEARCH_INDEX_STALE_DISPATCH_MS) const rows = await tx .select({ - workspaceId: workspaceFileSearchRevision.workspaceId, + entityType: sql`coalesce(${workspaceFileSearchRevision.entityType}, 'workspace')`, + entityId: sql`coalesce(${workspaceFileSearchRevision.entityId}, ${workspaceFileSearchRevision.workspaceId})`, fileId: workspaceFileSearchRevision.fileId, sourceContentUpdatedAt: workspaceFileSearchRevision.sourceContentUpdatedAt, currentFileId: workspaceFiles.id, @@ -285,8 +299,9 @@ async function reapStaleClaims( workspaceFiles, and( eq(workspaceFiles.id, workspaceFileSearchRevision.fileId), - eq(workspaceFiles.workspaceId, workspaceFileSearchRevision.workspaceId), - eq(workspaceFiles.context, 'workspace'), + sql`coalesce(${workspaceFiles.entityType}, 'workspace') = coalesce(${workspaceFileSearchRevision.entityType}, 'workspace')`, + sql`coalesce(${workspaceFiles.entityId}, ${workspaceFiles.workspaceId}) = coalesce(${workspaceFileSearchRevision.entityId}, ${workspaceFileSearchRevision.workspaceId})`, + sql`${workspaceFiles.context} = coalesce(${workspaceFileSearchRevision.entityType}, 'workspace')`, isNull(workspaceFiles.deletedAt), eq(workspaceFiles.contentUpdatedAt, workspaceFileSearchRevision.sourceContentUpdatedAt) ) @@ -313,11 +328,7 @@ async function reapStaleClaims( .update(workspaceFileSearchRevision) .set({ dispatchedAt: null, handoffExpiresAt: null, updatedAt: now }) .where(currentFilter) - await enqueueWorkspaces( - tx, - current.map((row) => row.workspaceId), - now - ) + await enqueueOwners(tx, current.map(rowOwner), now) } const obsoleteFilter = revisionFilter(obsolete) if (obsoleteFilter) { @@ -336,113 +347,149 @@ async function reapStaleClaims( * cannot be flattened into that join, so the claim stays an ordered walk of the pending index * that stops after the batch size. */ -async function claimQueuedWorkspaceJobs( +async function reconcileOwnerDependencies( + tx: DbTransaction, + owners: readonly EditableFileOwner[], + now: Date +) { + for (const owner of owners) { + const stale = await tx + .select({ fileId: workspaceFiles.id, buildId: workspaceFileSearchRevision.buildId }) + .from(workspaceFileSearchRevision) + .innerJoin(workspaceFiles, eq(workspaceFiles.id, workspaceFileSearchRevision.fileId)) + .where( + and( + sql`coalesce(${workspaceFileSearchRevision.entityType}, 'workspace') = ${owner.entityType}`, + sql`coalesce(${workspaceFileSearchRevision.entityId}, ${workspaceFileSearchRevision.workspaceId}) = ${owner.entityId}`, + eq(workspaceFileSearchRevision.status, 'ready'), + sql`NOT (${currentFileSearchDependencies(workspaceFileSearchRevision.buildId, owner)})` + ) + ) + .orderBy(asc(workspaceFiles.id)) + .limit(FILE_SEARCH_BACKFILL_PAGE_SIZE) + .for('update', { of: workspaceFiles, skipLocked: true }) + for (const row of stale) { + if (!row.buildId) continue + await tx + .update(workspaceFileSearchBuild) + .set({ expiresAt: now }) + .where(eq(workspaceFileSearchBuild.id, row.buildId)) + await tx + .update(workspaceFileSearchRevision) + .set({ + status: 'pending', + buildId: null, + dispatchedAt: null, + handoffExpiresAt: null, + failureReason: null, + lineCount: 0, + chunkCount: 0, + indexedBytes: 0, + updatedAt: now, + }) + .where( + and( + eq(workspaceFileSearchRevision.fileId, row.fileId), + eq(workspaceFileSearchRevision.buildId, row.buildId) + ) + ) + } + } +} + +async function claimQueuedOwnerJobs( tx: DbTransaction, - workspaceIds: readonly string[], + owners: readonly EditableFileOwner[], remainingGlobalCapacity: number, now: Date ): Promise { - if (workspaceIds.length === 0 || remainingGlobalCapacity <= 0) return [] - const workspaceValues = sql.join( - workspaceIds.map((workspaceId) => sql`(${workspaceId})`), + if (owners.length === 0 || remainingGlobalCapacity <= 0) return [] + const ownerValues = sql.join( + owners.map((owner) => sql`(${owner.entityType}, ${owner.entityId})`), sql`, ` ) const rows = await tx.execute<{ - workspaceId: string + entityType: string + entityId: string fileId: string sourceContentUpdatedAt: string }>(sql` - WITH selected_workspace(workspace_id) AS ( - VALUES ${workspaceValues} - ), - candidates AS MATERIALIZED ( - SELECT queued.* - FROM selected_workspace AS selected + WITH selected_owner(entity_type, entity_id) AS (VALUES ${ownerValues}), candidates AS MATERIALIZED ( + SELECT queued.* FROM selected_owner selected CROSS JOIN LATERAL ( - SELECT count(*)::int AS active_count - FROM ( - SELECT 1 FROM workspace_file_search_revision AS active - WHERE active.workspace_id = selected.workspace_id + SELECT count(*)::int AS active_count FROM ( + SELECT 1 FROM workspace_file_search_revision active + WHERE coalesce(active.entity_type, 'workspace') = selected.entity_type + AND coalesce(active.entity_id, active.workspace_id) = selected.entity_id AND active.status = 'pending' AND active.dispatched_at IS NOT NULL LIMIT ${FILE_SEARCH_INDEX_WORKSPACE_OUTSTANDING} - ) AS active_claims - ) AS workspace_active + ) active_claims + ) owner_active CROSS JOIN LATERAL ( - SELECT search_index.workspace_id, search_index.file_id, - search_index.source_content_updated_at, search_index.updated_at - FROM workspace_file_search_revision AS search_index + SELECT search_index.file_id, search_index.source_content_updated_at, search_index.updated_at, + coalesce(search_index.entity_type, 'workspace') entity_type, coalesce(search_index.entity_id, search_index.workspace_id) entity_id + FROM workspace_file_search_revision search_index CROSS JOIN LATERAL ( - SELECT 1 FROM workspace_files AS file - WHERE file.id = search_index.file_id - AND file.workspace_id = search_index.workspace_id - AND file.context = 'workspace' - AND file.deleted_at IS NULL - AND file.content_updated_at = search_index.source_content_updated_at + SELECT 1 FROM workspace_files file WHERE file.id = search_index.file_id + AND file.context = selected.entity_type + AND coalesce(file.entity_type, 'workspace') = selected.entity_type + AND coalesce(file.entity_id, file.workspace_id) = selected.entity_id + AND file.deleted_at IS NULL AND file.content_updated_at = search_index.source_content_updated_at LIMIT 1 - ) AS live_file - WHERE search_index.workspace_id = selected.workspace_id + ) live_file + WHERE coalesce(search_index.entity_type, 'workspace') = selected.entity_type + AND coalesce(search_index.entity_id, search_index.workspace_id) = selected.entity_id AND search_index.status = 'pending' AND search_index.dispatched_at IS NULL ORDER BY search_index.updated_at, search_index.file_id, search_index.source_content_updated_at - LIMIT greatest(0, ${FILE_SEARCH_INDEX_WORKSPACE_OUTSTANDING} - workspace_active.active_count) + LIMIT greatest(0, ${FILE_SEARCH_INDEX_WORKSPACE_OUTSTANDING} - owner_active.active_count) FOR UPDATE OF search_index SKIP LOCKED - ) AS queued - ORDER BY queued.updated_at, queued.workspace_id, queued.file_id, queued.source_content_updated_at + ) queued + ORDER BY queued.updated_at, queued.entity_type, queued.entity_id, queued.file_id, queued.source_content_updated_at LIMIT ${remainingGlobalCapacity} - ) - UPDATE workspace_file_search_revision AS search_index - SET dispatched_at = ${now.toISOString()}::timestamp, - handoff_expires_at = clock_timestamp() + ${FILE_SEARCH_DISPATCH_HANDOFF_MS} * interval '1 millisecond' - FROM candidates - WHERE search_index.file_id = candidates.file_id - AND search_index.source_content_updated_at = candidates.source_content_updated_at - AND search_index.status = 'pending' - AND search_index.dispatched_at IS NULL - RETURNING - search_index.workspace_id AS "workspaceId", - search_index.file_id AS "fileId", - search_index.source_content_updated_at AT TIME ZONE 'UTC' AS "sourceContentUpdatedAt" + ) UPDATE workspace_file_search_revision search_index + SET dispatched_at = ${now.toISOString()}::timestamp, + handoff_expires_at = clock_timestamp() + ${FILE_SEARCH_DISPATCH_HANDOFF_MS} * interval '1 millisecond' + FROM candidates WHERE search_index.file_id = candidates.file_id + AND search_index.source_content_updated_at = candidates.source_content_updated_at + AND search_index.status = 'pending' AND search_index.dispatched_at IS NULL + RETURNING coalesce(search_index.entity_type, 'workspace') AS "entityType", + coalesce(search_index.entity_id, search_index.workspace_id) AS "entityId", + search_index.file_id AS "fileId", search_index.source_content_updated_at AT TIME ZONE 'UTC' AS "sourceContentUpdatedAt" `) - - const remainingForWorkspace = tx + const remaining = tx .select({ fileId: workspaceFileSearchRevision.fileId }) .from(workspaceFileSearchRevision) - .innerJoin( - workspaceFiles, - and( - eq(workspaceFiles.id, workspaceFileSearchRevision.fileId), - eq(workspaceFiles.workspaceId, workspaceFileSearchDispatchQueue.workspaceId), - eq(workspaceFiles.context, 'workspace'), - isNull(workspaceFiles.deletedAt), - eq(workspaceFiles.contentUpdatedAt, workspaceFileSearchRevision.sourceContentUpdatedAt) - ) - ) .where( and( - eq(workspaceFileSearchRevision.workspaceId, workspaceFileSearchDispatchQueue.workspaceId), - eq(workspaceFileSearchRevision.status, 'pending'), - isNull(workspaceFileSearchRevision.dispatchedAt) + sql`coalesce(${workspaceFileSearchRevision.entityType}, 'workspace') = ${fileSearchDispatchQueue.entityType}`, + sql`coalesce(${workspaceFileSearchRevision.entityId}, ${workspaceFileSearchRevision.workspaceId}) = ${fileSearchDispatchQueue.entityId}`, + or( + and( + eq(workspaceFileSearchRevision.status, 'pending'), + isNull(workspaceFileSearchRevision.dispatchedAt) + ), + and( + eq(workspaceFileSearchRevision.status, 'ready'), + sql`EXISTS (SELECT 1 FROM file_search_dependency dependency LEFT JOIN workspace_files input ON input.id = dependency.file_id WHERE dependency.build_id = ${workspaceFileSearchRevision.buildId} AND (input.id IS NULL OR input.deleted_at IS NOT NULL OR input.key <> dependency.key OR input.content_updated_at <> dependency.source_content_updated_at))` + ) + ) ) ) - await tx - .update(workspaceFileSearchDispatchQueue) - .set({ lastDispatchedAt: now, updatedAt: now }) - .where( + const selected = or( + ...owners.map((owner) => and( - inArray(workspaceFileSearchDispatchQueue.workspaceId, workspaceIds), - exists(remainingForWorkspace) + eq(fileSearchDispatchQueue.entityType, owner.entityType), + eq(fileSearchDispatchQueue.entityId, owner.entityId) ) ) + ) await tx - .delete(workspaceFileSearchDispatchQueue) - .where( - and( - inArray(workspaceFileSearchDispatchQueue.workspaceId, workspaceIds), - notExists(remainingForWorkspace) - ) - ) - + .update(fileSearchDispatchQueue) + .set({ lastDispatchedAt: now, updatedAt: now }) + .where(and(selected, exists(remaining))) + await tx.delete(fileSearchDispatchQueue).where(and(selected, notExists(remaining))) return rows.map((row) => ({ - workspaceId: row.workspaceId, + owner: rowOwner(row), fileId: row.fileId, sourceContentUpdatedAt: new Date(row.sourceContentUpdatedAt).toISOString(), dispatchToken: now.toISOString(), @@ -522,24 +569,23 @@ export async function prepareWorkspaceFileSearchDispatch( } } - const workspaces = await tx - .select({ workspaceId: workspaceFileSearchDispatchQueue.workspaceId }) - .from(workspaceFileSearchDispatchQueue) + const queued = await tx + .select({ + entityType: fileSearchDispatchQueue.entityType, + entityId: fileSearchDispatchQueue.entityId, + }) + .from(fileSearchDispatchQueue) .orderBy( - sql`${workspaceFileSearchDispatchQueue.lastDispatchedAt} ASC NULLS FIRST`, - asc(workspaceFileSearchDispatchQueue.enqueuedAt), - asc(workspaceFileSearchDispatchQueue.workspaceId) + sql`${fileSearchDispatchQueue.lastDispatchedAt} ASC NULLS FIRST`, + asc(fileSearchDispatchQueue.enqueuedAt), + asc(fileSearchDispatchQueue.entityType), + asc(fileSearchDispatchQueue.entityId) ) .limit(Math.min(FILE_SEARCH_INDEX_DISPATCH_WORKSPACES, remainingGlobalCapacity)) - .for('update', { skipLocked: true }) - + const owners = await lockFileSearchOwners(tx, queued.map(rowOwner), { skipBusy: true }) + await reconcileOwnerDependencies(tx, owners, now) const payloads = await runDispatchPhase('claim', () => - claimQueuedWorkspaceJobs( - tx, - workspaces.map((workspace) => workspace.workspaceId), - remainingGlobalCapacity, - now - ) + claimQueuedOwnerJobs(tx, owners, remainingGlobalCapacity, now) ) return { payloads, backfilledFiles, reapedClaims, abandonedClaims, lockAcquired: true } }) @@ -549,7 +595,7 @@ export async function prepareWorkspaceFileSearchDispatch( function dispatchedRevisions(payloads: readonly WorkspaceFileSearchIndexPayload[]) { return payloads.map((payload) => ({ - workspaceId: payload.workspaceId, + owner: resolveFileSearchOwner(payload), fileId: payload.fileId, sourceContentUpdatedAt: new Date(payload.sourceContentUpdatedAt), dispatchToken: payload.dispatchToken, @@ -568,9 +614,9 @@ async function releaseDispatchClaims(payloads: readonly WorkspaceFileSearchIndex .set({ dispatchedAt: null, handoffExpiresAt: null, updatedAt: new Date() }) .where(and(filter, eq(workspaceFileSearchRevision.status, 'pending'))) } - await enqueueWorkspaces( + await enqueueOwners( tx, - rows.map((row) => row.workspaceId), + rows.map((row) => row.owner), new Date() ) }) diff --git a/apps/sim/lib/workspace-files/search/extract.test.ts b/apps/sim/lib/workspace-files/search/extract.test.ts index 2250b97c3ea..824ac623d2f 100644 --- a/apps/sim/lib/workspace-files/search/extract.test.ts +++ b/apps/sim/lib/workspace-files/search/extract.test.ts @@ -1,48 +1,17 @@ import { fileParsersMock, fileParsersMockFns } from '@sim/testing/mocks/file-parsers.mock' -import { - workspaceUploadsMock, - workspaceUploadsMockFns, -} from '@sim/testing/mocks/workspace-uploads.mock' import { beforeEach, describe, expect, it, vi } from 'vitest' -const { mockResolveServableDoc } = vi.hoisted(() => ({ - mockResolveServableDoc: vi.fn(), -})) - -vi.mock('@/lib/uploads/contexts/workspace', () => workspaceUploadsMock) -vi.mock('@/lib/mothership/tools/server/files/doc-compile', () => ({ - resolveServableDoc: mockResolveServableDoc, -})) vi.mock('@/lib/file-parsers', () => fileParsersMock) -import { assertKnownSizeWithinLimit, isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' +import { assertKnownSizeWithinLimit } from '@/lib/core/utils/stream-limits' import { FileParserError } from '@/lib/file-parsers/errors' -import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace' -import { - FILE_SEARCH_MAX_EXTRACTED_BYTES, - FILE_SEARCH_MAX_SOURCE_BYTES, -} from '@/lib/workspace-files/search/constants' -import { extractIndexText, loadIndexableBytes } from '@/lib/workspace-files/search/extract' +import { FILE_SEARCH_MAX_EXTRACTED_BYTES } from '@/lib/workspace-files/search/constants' +import { extractIndexText } from '@/lib/workspace-files/search/extract' const mockIsSupportedFileType = fileParsersMockFns.mockIsSupportedFileType mockIsSupportedFileType.mockReturnValue(false) const mockParseBuffer = fileParsersMockFns.mockParseBuffer -const mockFetchWorkspaceFileBuffer = workspaceUploadsMockFns.mockFetchWorkspaceFileBuffer - -const FILE: WorkspaceFileRecord = { - id: 'file-1', - workspaceId: 'workspace-1', - name: 'report.docx', - key: 'workspace/workspace-1/report.docx', - path: '/api/files/serve/workspace/workspace-1/report.docx', - size: 12, - type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - uploadedBy: 'user-1', - uploadedAt: new Date('2026-09-01T00:00:00.000Z'), - updatedAt: new Date('2026-09-01T00:00:00.000Z'), -} - const SOURCE = Buffer.from('const doc = new docx.Document({ sections: [] })', 'utf-8') const FENCED_JSON = Buffer.from('```json\n[\n { "a": 1 }\n]\n```\n', 'utf-8') const BINARY = Buffer.from([0x50, 0x4b, 0x03, 0x04, 0x00, 0x00, 0xff, 0xfe]) @@ -56,66 +25,6 @@ function sizeLimitError(): unknown { throw new Error('assertKnownSizeWithinLimit did not throw') } -describe('loadIndexableBytes', () => { - beforeEach(() => { - mockFetchWorkspaceFileBuffer.mockResolvedValue(SOURCE) - }) - - it('reads the compiled artifact of a generated document', async () => { - const artifact = Buffer.from('PKcompiled') - mockResolveServableDoc.mockResolvedValue({ - kind: 'artifact', - buffer: artifact, - contentType: FILE.type, - }) - const signal = new AbortController().signal - - await expect(loadIndexableBytes(FILE, signal)).resolves.toEqual({ - buffer: artifact, - kind: 'artifact', - }) - expect(mockFetchWorkspaceFileBuffer).toHaveBeenCalledWith(FILE, { - maxBytes: FILE_SEARCH_MAX_SOURCE_BYTES, - signal, - }) - expect(mockResolveServableDoc).toHaveBeenCalledWith(FILE.workspaceId, SOURCE, FILE.name, { - maxBytes: FILE_SEARCH_MAX_SOURCE_BYTES, - signal, - }) - }) - - it('settles for the generation source when no artifact exists, without compiling', async () => { - mockResolveServableDoc.mockResolvedValue({ kind: 'unavailable' }) - - await expect(loadIndexableBytes(FILE, new AbortController().signal)).resolves.toEqual({ - buffer: SOURCE, - kind: 'source', - }) - }) - - it('refuses an artifact above the source ceiling as a size-limit breach', async () => { - mockResolveServableDoc.mockResolvedValue({ - kind: 'artifact', - buffer: Buffer.alloc(FILE_SEARCH_MAX_SOURCE_BYTES + 1), - contentType: FILE.type, - }) - - await expect(loadIndexableBytes(FILE, new AbortController().signal)).rejects.toSatisfy( - isPayloadSizeLimitError - ) - }) - - it('stops before resolving once the run is aborted', async () => { - const controller = new AbortController() - controller.abort() - - await expect(loadIndexableBytes(FILE, controller.signal)).rejects.toMatchObject({ - name: 'AbortError', - }) - expect(mockResolveServableDoc).not.toHaveBeenCalled() - }) -}) - describe('extractIndexText', () => { beforeEach(() => { mockIsSupportedFileType.mockReturnValue(true) diff --git a/apps/sim/lib/workspace-files/search/extract.ts b/apps/sim/lib/workspace-files/search/extract.ts index 0e10beab8f8..08238ee876b 100644 --- a/apps/sim/lib/workspace-files/search/extract.ts +++ b/apps/sim/lib/workspace-files/search/extract.ts @@ -1,19 +1,11 @@ import { Buffer, isUtf8 } from 'node:buffer' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' -import { assertKnownSizeWithinLimit, isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' +import { isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' import { isSupportedFileType } from '@/lib/file-parsers' import { getFileParserErrorCode } from '@/lib/file-parsers/errors' -import { resolveServableDoc } from '@/lib/mothership/tools/server/files/doc-compile' -import { - fetchWorkspaceFileBuffer, - type WorkspaceFileRecord, -} from '@/lib/uploads/contexts/workspace' import { getFileExtension } from '@/lib/uploads/utils/file-utils' -import { - FILE_SEARCH_MAX_EXTRACTED_BYTES, - FILE_SEARCH_MAX_SOURCE_BYTES, -} from '@/lib/workspace-files/search/constants' +import { FILE_SEARCH_MAX_EXTRACTED_BYTES } from '@/lib/workspace-files/search/constants' import { FileSearchExclusionError } from '@/lib/workspace-files/search/index-plan' import { parseWorkspaceFileText } from '@/lib/workspace-files/text-extraction' @@ -37,39 +29,6 @@ export interface ExtractedIndexText { partial: boolean } -/** - * Loads the bytes to index without executing anything. - * - * Generated documents store their generation source as the primary file and keep the rendered - * binary in the compiled-artifact store. This reads the artifact when it exists and otherwise - * settles for the source text, the same read-only resolution the public share route uses. - * Compile-on-read belongs to download surfaces acting for a principal: an indexer running as - * nobody inside a worker must not run a document's source, whatever compiler that worker has. - */ -export async function loadIndexableBytes( - file: WorkspaceFileRecord, - signal: AbortSignal -): Promise { - const raw = await fetchWorkspaceFileBuffer(file, { - maxBytes: FILE_SEARCH_MAX_SOURCE_BYTES, - signal, - }) - signal.throwIfAborted() - const servable = await resolveServableDoc(file.workspaceId, raw, file.name, { - maxBytes: FILE_SEARCH_MAX_SOURCE_BYTES, - signal, - }) - if (servable.kind === 'artifact') { - assertKnownSizeWithinLimit( - servable.buffer.length, - FILE_SEARCH_MAX_SOURCE_BYTES, - 'search index artifact' - ) - return { buffer: servable.buffer, kind: 'artifact' } - } - return { buffer: raw, kind: servable.kind === 'unavailable' ? 'source' : 'stored' } -} - function isPlainText(buffer: Buffer): boolean { return isUtf8(buffer) && !buffer.includes(0) } diff --git a/apps/sim/lib/workspace-files/search/index-state.ts b/apps/sim/lib/workspace-files/search/index-state.ts index 02835dbeb95..8a81993c9ae 100644 --- a/apps/sim/lib/workspace-files/search/index-state.ts +++ b/apps/sim/lib/workspace-files/search/index-state.ts @@ -1,12 +1,13 @@ import { db } from '@sim/db' import { + fileSearchDependency, workspaceFileSearchBuild, workspaceFileSearchChunk, workspaceFileSearchRevision, workspaceFiles, } from '@sim/db/schema' import { generateId } from '@sim/utils/id' -import { and, eq, sql } from 'drizzle-orm' +import { and, asc, eq, inArray, sql } from 'drizzle-orm' import type { DbTransaction } from '@/lib/db/types' import { FILE_SEARCH_BUILD_LEASE_MS, @@ -17,47 +18,83 @@ import { FILE_SEARCH_CLEANUP_MAX_BATCHES, FILE_SEARCH_CLEANUP_MIN_BATCH_MS, FILE_SEARCH_INDEX_TRANSACTION_LIMITS, + FILE_SEARCH_MAX_DEPENDENCIES, } from '@/lib/workspace-files/search/constants' import { exceedsFileSearchBatchBudget } from '@/lib/workspace-files/search/index-batches' import { estimateTrigramKeys, type FileSearchChunk } from '@/lib/workspace-files/search/index-plan' +import { lockFileSearchOwners } from '@/lib/workspace-files/search/owner-policy' +import { + type FileSearchOwnerScope, + fileSearchOwnerCondition, + fileSearchOwnerFields, + resolveFileSearchOwner, + searchableFileCondition, +} from '@/lib/workspace-files/search/scope' +import type { FileSearchDependencyIdentity } from '@/lib/workspace-files/search/source' import { configureFileSearchTransaction } from '@/lib/workspace-files/search/transaction' -export interface FileSearchRevision { - workspaceId: string +export type FileSearchRevision = FileSearchOwnerScope & { fileId: string sourceContentUpdatedAt: Date } -export interface FileSearchBuild extends FileSearchRevision { - id: string -} +export type FileSearchBuild = FileSearchRevision & { id: string } function revisionFilter(revision: FileSearchRevision) { return and( eq(workspaceFileSearchRevision.fileId, revision.fileId), - eq(workspaceFileSearchRevision.workspaceId, revision.workspaceId), + fileSearchOwnerCondition(workspaceFileSearchRevision, resolveFileSearchOwner(revision)), eq(workspaceFileSearchRevision.sourceContentUpdatedAt, revision.sourceContentUpdatedAt) ) } -async function lockCurrentFile(tx: DbTransaction, revision: FileSearchRevision): Promise { - const [file] = await tx +export async function lockFileSearchInputs( + tx: DbTransaction, + revision: FileSearchRevision, + dependencies: readonly FileSearchDependencyIdentity[] = [], + sourceKey?: string +): Promise { + const owner = resolveFileSearchOwner(revision) + if ( + dependencies.length > FILE_SEARCH_MAX_DEPENDENCIES || + new Set(dependencies.map((entry) => entry.fileId)).size !== dependencies.length + ) + throw new Error('Invalid file search dependency manifest') + await lockFileSearchOwners(tx, [owner]) + const files = await tx .select({ - workspaceId: workspaceFiles.workspaceId, - context: workspaceFiles.context, + id: workspaceFiles.id, + key: workspaceFiles.key, deletedAt: workspaceFiles.deletedAt, contentUpdatedAt: workspaceFiles.contentUpdatedAt, }) .from(workspaceFiles) - .where(eq(workspaceFiles.id, revision.fileId)) + .where( + and( + searchableFileCondition(owner), + inArray(workspaceFiles.id, [ + ...new Set([revision.fileId, ...dependencies.map((entry) => entry.fileId)]), + ]) + ) + ) + .orderBy(asc(workspaceFiles.id)) .for('update') - .limit(1) + const byId = new Map(files.map((file) => [file.id, file])) + const file = byId.get(revision.fileId) return Boolean( file && - file.workspaceId === revision.workspaceId && - file.context === 'workspace' && file.deletedAt === null && - file.contentUpdatedAt.getTime() === revision.sourceContentUpdatedAt.getTime() + (sourceKey === undefined || file.key === sourceKey) && + file.contentUpdatedAt.getTime() === revision.sourceContentUpdatedAt.getTime() && + dependencies.every((entry) => { + const current = byId.get(entry.fileId) + return ( + current && + current.deletedAt === null && + current.key === entry.key && + current.contentUpdatedAt.getTime() === entry.sourceContentUpdatedAt.getTime() + ) + }) ) } @@ -96,7 +133,7 @@ export async function beginFileSearchBuild( ): Promise { return db.transaction(async (tx) => { await configureFileSearchTransaction(tx, FILE_SEARCH_INDEX_TRANSACTION_LIMITS) - if (!(await lockCurrentFile(tx, revision))) return null + if (!(await lockFileSearchInputs(tx, revision))) return null const [observed] = await tx .select({ buildId: workspaceFileSearchRevision.buildId, @@ -124,7 +161,10 @@ export async function beginFileSearchBuild( const build = { ...revision, id: generateId() } const now = new Date() await tx.insert(workspaceFileSearchBuild).values({ - ...build, + id: build.id, + fileId: build.fileId, + sourceContentUpdatedAt: build.sourceContentUpdatedAt, + ...fileSearchOwnerFields(resolveFileSearchOwner(build)), expiresAt: sql`clock_timestamp() + ${FILE_SEARCH_BUILD_LEASE_MS} * interval '1 millisecond'`, }) await tx @@ -166,17 +206,26 @@ export async function appendFileSearchChunks( await configureFileSearchTransaction(tx, FILE_SEARCH_INDEX_TRANSACTION_LIMITS) if (!(await lockBuild(tx, build))) return false signal.throwIfAborted() - await tx - .insert(workspaceFileSearchChunk) - .values( - chunks.map((chunk) => ({ ...chunk, buildId: build.id, workspaceId: build.workspaceId })) - ) + await tx.insert(workspaceFileSearchChunk).values( + chunks.map((chunk) => ({ + ...chunk, + buildId: build.id, + ...fileSearchOwnerFields(resolveFileSearchOwner(build)), + })) + ) return true }) } export type FileSearchPublication = - | { status: 'ready'; lineCount: number; indexedBytes: number; chunkCount: number } + | { + status: 'ready' + lineCount: number + indexedBytes: number + chunkCount: number + dependencies?: readonly FileSearchDependencyIdentity[] + artifactKey?: string + } | { status: 'skipped'; failureReason: string } /** Publication changes one pointer only after every chunk is durable and the file is still current. */ @@ -188,7 +237,15 @@ export async function publishFileSearchBuild( return db.transaction(async (tx) => { await configureFileSearchTransaction(tx, FILE_SEARCH_INDEX_TRANSACTION_LIMITS) signal.throwIfAborted() - if (!(await lockCurrentFile(tx, build)) || !(await lockBuild(tx, build))) return false + if ( + !(await lockFileSearchInputs( + tx, + build, + publication.status === 'ready' ? publication.dependencies : [] + )) || + !(await lockBuild(tx, build)) + ) + return false if (publication.status === 'ready') { const [stored] = await tx.execute<{ count: number; bytes: number }>(sql` SELECT count(*)::int AS count, @@ -199,9 +256,19 @@ export async function publishFileSearchBuild( throw new Error('File search build is incomplete') } } + if (publication.status === 'ready' && publication.dependencies?.length) { + await tx + .insert(fileSearchDependency) + .values( + publication.dependencies.map((dependency) => ({ ...dependency, buildId: build.id })) + ) + } await tx .update(workspaceFileSearchBuild) - .set({ expiresAt: publication.status === 'ready' ? null : sql`clock_timestamp()` }) + .set({ + expiresAt: publication.status === 'ready' ? null : sql`clock_timestamp()`, + artifactKey: publication.status === 'ready' ? (publication.artifactKey ?? null) : null, + }) .where(eq(workspaceFileSearchBuild.id, build.id)) await tx .update(workspaceFileSearchRevision) @@ -226,7 +293,7 @@ export async function failFileSearchRevision( ): Promise { await db.transaction(async (tx) => { await configureFileSearchTransaction(tx, FILE_SEARCH_INDEX_TRANSACTION_LIMITS) - if (!(await lockCurrentFile(tx, revision))) return + if (!(await lockFileSearchInputs(tx, revision))) return const [state] = await tx .select() .from(workspaceFileSearchRevision) diff --git a/apps/sim/lib/workspace-files/search/indexing.test.ts b/apps/sim/lib/workspace-files/search/indexing.test.ts index 44b53f71333..079b9a48307 100644 --- a/apps/sim/lib/workspace-files/search/indexing.test.ts +++ b/apps/sim/lib/workspace-files/search/indexing.test.ts @@ -1,8 +1,3 @@ -import { createLogger } from '@sim/logger' -import { - workspaceUploadsMock, - workspaceUploadsMockFns, -} from '@sim/testing/mocks/workspace-uploads.mock' import { DrizzleQueryError } from 'drizzle-orm/errors' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -20,12 +15,14 @@ vi.mock('@/lib/workspace-files/search/index-state', () => ({ publishFileSearchBuild: hoisted.publish, failFileSearchRevision: hoisted.fail, })) -vi.mock('@/lib/uploads/contexts/workspace', () => workspaceUploadsMock) +vi.mock('@/lib/workspace-files/search/source', () => ({ + loadFileSearchSource: hoisted.load, +})) vi.mock('@/lib/workspace-files/search/extract', () => ({ - loadIndexableBytes: hoisted.load, extractIndexText: hoisted.extract, })) +import { assertKnownSizeWithinLimit } from '@/lib/core/utils/stream-limits' import { FILE_SEARCH_INDEX_CAPACITY_MAX_ATTEMPTS, FILE_SEARCH_INDEX_CAPACITY_RETRY_BASE_MS, @@ -40,14 +37,7 @@ import { indexWorkspaceFileForSearch, } from '@/lib/workspace-files/search/indexing' -const mocks = { - ...hoisted, - file: workspaceUploadsMockFns.mockGetWorkspaceFile, -} - -const logger = vi.mocked(createLogger).mock.results[ - vi.mocked(createLogger).mock.calls.findIndex(([name]) => name === 'WorkspaceFileSearchIndexer') -].value as { info: ReturnType; warn: ReturnType } +const mocks = hoisted const FILE_TEXT = 'confidential customer text' @@ -76,12 +66,11 @@ describe('complete-file indexing worker', () => { mocks.begin.mockResolvedValue({ id: 'build', ...payload }) mocks.append.mockResolvedValue(true) mocks.publish.mockResolvedValue(true) - mocks.file.mockResolvedValue({ - name: 'sample.txt', - size: 6, - contentUpdatedAt: new Date(payload.sourceContentUpdatedAt), + mocks.load.mockResolvedValue({ + file: { originalName: 'sample.txt' }, + bytes: { buffer: Buffer.from('needle'), kind: 'stored' }, + dependencies: [], }) - mocks.load.mockResolvedValue({ buffer: Buffer.from('needle') }) mocks.extract.mockResolvedValue({ text: 'needle', partial: false }) }) it('does no storage work for an obsolete dispatch', async () => { @@ -89,18 +78,17 @@ describe('complete-file indexing worker', () => { await indexWorkspaceFileForSearch(payload, signal) expect(mocks.load).not.toHaveBeenCalled() expect(mocks.publish).not.toHaveBeenCalled() - expect(logger.info).toHaveBeenCalledWith( - 'Workspace file search run no longer owns its revision', - payload - ) }) - it('rejects an oversized source before downloading', async () => { - mocks.file.mockResolvedValue({ - size: FILE_SEARCH_MAX_SOURCE_BYTES + 1, - contentUpdatedAt: new Date(payload.sourceContentUpdatedAt), + it('excludes a source size-limit breach without writing chunks', async () => { + mocks.load.mockImplementationOnce(() => { + assertKnownSizeWithinLimit( + FILE_SEARCH_MAX_SOURCE_BYTES + 1, + FILE_SEARCH_MAX_SOURCE_BYTES, + 'search source' + ) }) await indexWorkspaceFileForSearch(payload, signal) - expect(mocks.load).not.toHaveBeenCalled() + expect(mocks.append).not.toHaveBeenCalled() expect(mocks.publish).toHaveBeenCalledWith( expect.anything(), { status: 'skipped', failureReason: 'source_too_large' }, @@ -132,7 +120,13 @@ describe('complete-file indexing worker', () => { expect(rows).toBeLessThan(300) expect(mocks.publish).toHaveBeenCalledWith( expect.anything(), - { status: 'ready', chunkCount: rows, lineCount: 600000, indexedBytes: 2400000 }, + { + status: 'ready', + chunkCount: rows, + lineCount: 600000, + indexedBytes: 2400000, + dependencies: [], + }, signal ) expect(mocks.append.mock.invocationCallOrder.at(-1)).toBeLessThan( @@ -173,12 +167,11 @@ describe('indexing retry policy', () => { async function thrownBy(error: unknown): Promise { vi.clearAllMocks() mocks.begin.mockResolvedValue({ id: 'build', ...payload }) - mocks.file.mockResolvedValue({ - name: 'notes.txt', - size: 100, - contentUpdatedAt: new Date(payload.sourceContentUpdatedAt), + mocks.load.mockResolvedValue({ + file: { originalName: 'notes.txt' }, + bytes: { buffer: Buffer.from('a\n'), kind: 'stored' }, + dependencies: [], }) - mocks.load.mockResolvedValue({ buffer: Buffer.from('a\n') }) mocks.extract.mockResolvedValue({ text: 'a\n', lineCount: 1 }) mocks.append.mockRejectedValue(error) return indexWorkspaceFileForSearch(payload, signal).catch((thrown) => thrown) @@ -225,11 +218,6 @@ describe('indexing retry policy', () => { it('treats a reset outside the database as an ordinary failure', async () => { vi.clearAllMocks() mocks.begin.mockResolvedValue({ id: 'build', ...payload }) - mocks.file.mockResolvedValue({ - name: 'notes.txt', - size: 100, - contentUpdatedAt: new Date(payload.sourceContentUpdatedAt), - }) mocks.load.mockRejectedValue( Object.assign(new Error('read ECONNRESET'), { code: 'ECONNRESET' }) ) diff --git a/apps/sim/lib/workspace-files/search/indexing.ts b/apps/sim/lib/workspace-files/search/indexing.ts index b0552835242..0c65187dba8 100644 --- a/apps/sim/lib/workspace-files/search/indexing.ts +++ b/apps/sim/lib/workspace-files/search/indexing.ts @@ -1,6 +1,7 @@ import { Buffer } from 'node:buffer' import { createLogger } from '@sim/logger' import { describeError } from '@sim/utils/errors' +import { isRecordLike } from '@sim/utils/object' import { type BackgroundRetryDecision, type BackgroundRetryPolicy, @@ -8,16 +9,14 @@ import { } from '@/lib/core/errors/background-retry' import { redactDatabaseQueryError } from '@/lib/core/errors/database-query-error' import { isPayloadSizeLimitError } from '@/lib/core/utils/stream-limits' -import { getWorkspaceFile } from '@/lib/uploads/contexts/workspace' import { FILE_SEARCH_INDEX_CAPACITY_MAX_ATTEMPTS, FILE_SEARCH_INDEX_CAPACITY_RETRY_BASE_MS, FILE_SEARCH_INDEX_CAPACITY_RETRY_MAX_MS, FILE_SEARCH_INDEX_MAX_ATTEMPTS, - FILE_SEARCH_MAX_SOURCE_BYTES, FILE_SEARCH_SLOW_INSERT_BATCH_MS, } from '@/lib/workspace-files/search/constants' -import { extractIndexText, loadIndexableBytes } from '@/lib/workspace-files/search/extract' +import { extractIndexText } from '@/lib/workspace-files/search/extract' import { iterateFileSearchBatches } from '@/lib/workspace-files/search/index-batches' import { estimateTrigramKeys, @@ -34,11 +33,15 @@ import { failFileSearchRevision, publishFileSearchBuild, } from '@/lib/workspace-files/search/index-state' +import { + type FileSearchOwnerScope, + resolveFileSearchOwner, +} from '@/lib/workspace-files/search/scope' +import { loadFileSearchSource } from '@/lib/workspace-files/search/source' const logger = createLogger('WorkspaceFileSearchIndexer') -export interface WorkspaceFileSearchIndexPayload { - workspaceId: string +export type WorkspaceFileSearchIndexPayload = FileSearchOwnerScope & { fileId: string sourceContentUpdatedAt: string /** Identifies the dispatch claim so stale callbacks cannot change a newer run. */ @@ -49,7 +52,7 @@ function parseRevision(payload: WorkspaceFileSearchIndexPayload): FileSearchRevi const sourceContentUpdatedAt = new Date(payload.sourceContentUpdatedAt) if (Number.isNaN(sourceContentUpdatedAt.getTime())) throw new Error('Invalid workspace file search revision') - return { workspaceId: payload.workspaceId, fileId: payload.fileId, sourceContentUpdatedAt } + return { owner: resolveFileSearchOwner(payload), fileId: payload.fileId, sourceContentUpdatedAt } } /** @@ -69,7 +72,7 @@ async function appendTimedBatch( const durationMs = Date.now() - startedAt if (durationMs >= FILE_SEARCH_SLOW_INSERT_BATCH_MS) { logger.warn('Workspace file search insert batch was slow', { - workspaceId: build.workspaceId, + owner: resolveFileSearchOwner(build), fileId: build.fileId, buildId: build.id, firstOrdinal: batch[0]?.ordinal, @@ -83,10 +86,25 @@ async function appendTimedBatch( } export async function indexWorkspaceFileForSearch( - payload: WorkspaceFileSearchIndexPayload, + input: unknown, signal: AbortSignal ): Promise { signal.throwIfAborted() + const owner = resolveFileSearchOwner(input) + if ( + !isRecordLike(input) || + typeof input.fileId !== 'string' || + !input.fileId || + typeof input.sourceContentUpdatedAt !== 'string' || + (input.dispatchToken !== undefined && typeof input.dispatchToken !== 'string') + ) + throw new Error('Invalid file search payload') + const payload: WorkspaceFileSearchIndexPayload = { + owner, + fileId: input.fileId, + sourceContentUpdatedAt: input.sourceContentUpdatedAt, + dispatchToken: input.dispatchToken, + } if (!payload.dispatchToken) return const revision = parseRevision(payload) const build = await beginFileSearchBuild(revision, payload.dispatchToken) @@ -96,19 +114,10 @@ export async function indexWorkspaceFileForSearch( } const startedAt = Date.now() try { - const file = await getWorkspaceFile(payload.workspaceId, payload.fileId, { throwOnError: true }) - if (!file || file.contentUpdatedAt?.getTime() !== revision.sourceContentUpdatedAt.getTime()) - return - if (file.size > FILE_SEARCH_MAX_SOURCE_BYTES) { - await publishFileSearchBuild( - build, - { status: 'skipped', failureReason: 'source_too_large' }, - signal - ) - return - } - const bytes = await loadIndexableBytes(file, signal) - const extracted = await extractIndexText(bytes, file.name, signal) + const source = await loadFileSearchSource(revision, signal) + if (!source) return + const { file, bytes, dependencies, artifactKey } = source + const extracted = await extractIndexText(bytes, file.originalName, signal) if (!extracted) { await publishFileSearchBuild( build, @@ -126,7 +135,14 @@ export async function indexWorkspaceFileForSearch( } const published = await publishFileSearchBuild( build, - { status: 'ready', chunkCount, lineCount: plan.lineCount, indexedBytes: plan.indexedBytes }, + { + status: 'ready', + chunkCount, + lineCount: plan.lineCount, + indexedBytes: plan.indexedBytes, + dependencies, + ...(artifactKey ? { artifactKey } : {}), + }, signal ) logger.info('Workspace file search build completed', { diff --git a/apps/sim/lib/workspace-files/search/owner-policy.ts b/apps/sim/lib/workspace-files/search/owner-policy.ts new file mode 100644 index 00000000000..f30bcd41892 --- /dev/null +++ b/apps/sim/lib/workspace-files/search/owner-policy.ts @@ -0,0 +1,52 @@ +import { compareStrings } from '@sim/utils/string' +import { tryAcquireAdvisoryXactLock } from '@/lib/db/advisory-locks' +import type { DbTransaction } from '@/lib/db/types' +import { lockProject } from '@/lib/projects/membership' +import { + type FileOwnerAdapters, + requireFileOwnerAdapter, +} from '@/lib/workspace-files/owner-adapters' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' + +const policies: FileOwnerAdapters<{ + lock(tx: DbTransaction, entityId: string): Promise + tryLock(tx: DbTransaction, entityId: string): Promise +}> = { + workspace: { + async lock() {}, + async tryLock() { + return true + }, + }, + project: { + lock: lockProject, + tryLock: (tx, id) => tryAcquireAdvisoryXactLock(tx, 'file_search_dispatch', `project:${id}`), + }, +} + +/** Background work follows application lock order without acquiring a human's authority. */ +export async function lockFileSearchOwners( + tx: DbTransaction, + owners: readonly EditableFileOwner[], + options: { skipBusy?: boolean } = {} +): Promise { + const ordered = [ + ...new Map( + owners.map((owner) => [JSON.stringify([owner.entityType, owner.entityId]), owner]) + ).values(), + ] + ordered.sort( + (a, b) => compareStrings(a.entityType, b.entityType) || compareStrings(a.entityId, b.entityId) + ) + const acquired: EditableFileOwner[] = [] + for (const owner of ordered) { + const policy = requireFileOwnerAdapter(policies, owner) + if (options.skipBusy) { + if (!(await policy.tryLock(tx, owner.entityId))) continue + } else { + await policy.lock(tx, owner.entityId) + } + acquired.push(owner) + } + return acquired +} diff --git a/apps/sim/lib/workspace-files/search/owners.integration.ts b/apps/sim/lib/workspace-files/search/owners.integration.ts new file mode 100644 index 00000000000..59d811c5dbc --- /dev/null +++ b/apps/sim/lib/workspace-files/search/owners.integration.ts @@ -0,0 +1,824 @@ +import { mkdtempSync } from 'node:fs' +import { mkdir, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { dirname, join } from 'node:path' +import { db, runOutsideTransactionContext } from '@sim/db' +import { + folder, + member, + organization, + permissions, + project, + projectWorkspace, + user, + workspace, + workspaceFileSearchBuild, + workspaceFileSearchChunk, + workspaceFileSearchRevision, + workspaceFiles, +} from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' +import { createSessionPrincipal } from '@sim/testing/factories/principal.factory' +import { setUploadDirServer, uploadsSetupMock } from '@sim/testing/mocks/uploads-setup.mock' +import { getErrorMessage } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import { Document, Packer, Paragraph } from 'docx' +import { eq, inArray, sql } from 'drizzle-orm' +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' + +vi.mock('@/lib/uploads/core/setup.server', () => uploadsSetupMock) + +import { getFileContentProvenance } from '@/lib/internal/file/operations' +import { lockProject } from '@/lib/projects/membership' +import { listFileFolders } from '@/lib/uploads/contexts/workspace/workspace-file-folder-manager' +import { uploadWorkspaceFile } from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { storeCompiledDoc } from '@/lib/uploads/documents/compiled-store' +import { fileDocumentInputIdentity } from '@/lib/uploads/documents/input-identity' +import { searchWorkspaceFileContent } from '@/lib/workspace-files/application/search-workspace-file-content' +import { resolveFileSearchFolderScope } from '@/lib/workspace-files/search/delivery' +import { + appendFileSearchChunks, + beginFileSearchBuild, + publishFileSearchBuild, +} from '@/lib/workspace-files/search/index-state' +import { indexWorkspaceFileForSearch } from '@/lib/workspace-files/search/indexing' +import { compileFileSearchPattern } from '@/lib/workspace-files/search/pattern' +import { searchFileIndex } from '@/lib/workspace-files/search/repository' + +const storageRoot = mkdtempSync(join(tmpdir(), 'sim-file-owner-search-')) +setUploadDirServer(storageRoot) +const fixtures: { + ownerId: string + readerId: string + organizationId: string + workspaceId: string + projectId: string +}[] = [] +const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = + [] +const signal = new AbortController().signal + +beforeEach(() => { + vi.stubEnv('FREE_STORAGE_LIMIT_GB', '') +}) + +function check(name: string, run: () => Promise) { + it(name, async () => { + const started = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - started }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - started, + error: getErrorMessage(error), + }) + throw error + } + }) +} + +async function fixture() { + const ownerId = generateId() + const readerId = generateId() + const organizationId = generateId() + const workspaceId = generateId() + await db.insert(user).values( + [ownerId, readerId].map((id) => ({ + id, + email: `${id}@search.invalid`, + name: 'Search fixture', + emailVerified: true, + createdAt: new Date(), + updatedAt: new Date(), + })) + ) + await db + .insert(organization) + .values({ id: organizationId, name: 'Search', slug: organizationId, createdAt: new Date() }) + await db.insert(member).values( + [ownerId, readerId].map((userId) => ({ + id: generateId(), + organizationId, + userId, + role: userId === ownerId ? 'owner' : 'member', + createdAt: new Date(), + })) + ) + await insertWorkspaceFixture(db, { + id: workspaceId, + ownerId, + billedAccountUserId: ownerId, + organizationId, + workspaceMode: 'organization', + name: 'Search environment', + }) + const [binding] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, workspaceId)) + if (!binding) throw new Error('Missing Project binding') + await db.insert(permissions).values({ + id: generateId(), + userId: readerId, + entityType: 'workspace', + entityId: workspaceId, + permissionType: 'read', + }) + const ids = { ownerId, readerId, organizationId, workspaceId, projectId: binding.projectId } + fixtures.push(ids) + return { + ...ids, + principal: createSessionPrincipal({ userId: ownerId }), + reader: createSessionPrincipal({ userId: readerId }), + owner: { entityType: 'project' as const, entityId: binding.projectId }, + } +} + +async function create( + f: Awaited>, + content: string, + options: { name?: string; folderId?: string } = {} +) { + const id = generateId() + const name = options.name ?? 'architecture.md' + const key = `project/${f.projectId}/${id}/${name}` + await mkdir(dirname(join(storageRoot, key)), { recursive: true }) + await writeFile(join(storageRoot, key), content) + const [file] = await db + .insert(workspaceFiles) + .values({ + id, + userId: f.ownerId, + entityType: 'project', + entityId: f.projectId, + context: 'project', + key, + originalName: name, + contentType: 'text/plain', + sizeBytes: Buffer.byteLength(content), + folderId: options.folderId, + }) + .returning() + if (!file) throw new Error('Search source fixture missing') + return { file } +} + +async function claim(fileId: string) { + const token = new Date() + const [row] = await db + .update(workspaceFileSearchRevision) + .set({ dispatchedAt: token }) + .where(eq(workspaceFileSearchRevision.fileId, fileId)) + .returning() + if (!row) throw new Error('File trigger did not enqueue the search revision') + return { + fileId, + sourceContentUpdatedAt: row.sourceContentUpdatedAt.toISOString(), + dispatchToken: token.toISOString(), + } +} + +async function changeFile(fileId: string, content: string) { + const [old] = await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, fileId)) + if (!old) throw new Error('Search source fixture missing') + const key = `project/${old.entityId}/${generateId()}/${old.originalName}` + await mkdir(dirname(join(storageRoot, key)), { recursive: true }) + await writeFile(join(storageRoot, key), content) + await db + .update(workspaceFiles) + .set({ + key, + sizeBytes: Buffer.byteLength(content), + contentUpdatedAt: new Date(old.contentUpdatedAt.getTime() + 1_000), + }) + .where(eq(workspaceFiles.id, fileId)) +} + +async function createFolder( + f: Awaited>, + name: string, + parentId?: string +) { + const [record] = await db + .insert(folder) + .values({ + id: generateId(), + resourceType: 'file', + entityType: 'project', + entityId: f.projectId, + userId: f.ownerId, + name, + parentId, + }) + .returning() + if (!record) throw new Error('Search folder fixture missing') + return { folder: record } +} + +async function search( + f: Awaited>, + query: string, + options: { folderPaths?: string[]; includeSubfolders?: boolean } = {} +) { + const folders = await listFileFolders(f.owner) + return searchFileIndex({ + owner: f.owner, + pattern: compileFileSearchPattern(query, 'exact'), + maxResults: 100, + folderScope: resolveFileSearchFolderScope(folders, options), + signal, + }) +} + +describe('owner-scoped indexed file search on PostgreSQL and stored bytes', () => { + check( + 'fences duplicate legacy and owner-aware workspace workers to one published build', + async () => { + const f = await fixture() + const file = await uploadWorkspaceFile( + f.workspaceId, + f.ownerId, + Buffer.from('duplicateneedle'), + 'duplicate.txt', + 'text/plain', + { notifyWorkspaceChange: false } + ) + const claimed = await claim(file.id) + const identity = { + fileId: file.id, + sourceContentUpdatedAt: new Date(claimed.sourceContentUpdatedAt), + } + const builds = await Promise.all([ + beginFileSearchBuild({ workspaceId: f.workspaceId, ...identity }, claimed.dispatchToken), + beginFileSearchBuild( + { owner: { entityType: 'workspace', entityId: f.workspaceId }, ...identity }, + claimed.dispatchToken + ), + ]) + expect(builds.filter(Boolean)).toHaveLength(2) + const outcomes = [] + for (const build of builds) { + if (!build) throw new Error('Both compatible workers must resolve the canonical revision') + await appendFileSearchChunks( + build, + [{ ordinal: 0, lineStart: 1, fragment: false, overlap: 0, content: 'duplicateneedle' }], + signal + ) + outcomes.push( + await publishFileSearchBuild( + build, + { status: 'ready', lineCount: 1, indexedBytes: 15, chunkCount: 1 }, + signal + ) + ) + } + expect(outcomes.filter(Boolean)).toHaveLength(1) + const result = await searchWorkspaceFileContent.execute({ + principal: f.reader, + input: { + workspaceId: f.workspaceId, + query: 'duplicateneedle', + mode: 'exact', + maxResults: 100, + }, + }) + expect(result.results.map((hit) => hit.fileId)).toEqual([file.id]) + } + ) + + check('queues canonical Project revisions and isolates workspace and Project text', async () => { + const f = await fixture() + const projectFile = await create(f, 'projectneedle owner-isolated') + const workspaceFile = await uploadWorkspaceFile( + f.workspaceId, + f.ownerId, + Buffer.from('workspaceneedle owner-isolated'), + 'architecture.md', + 'text/plain', + { notifyWorkspaceChange: false } + ) + const projectClaim = await claim(projectFile.file.id) + const queue = await db.execute<{ entity_type: string; entity_id: string }>( + sql`SELECT entity_type, entity_id FROM file_search_dispatch_queue WHERE entity_type = 'project' AND entity_id = ${f.projectId}` + ) + expect(queue).toHaveLength(1) + await indexWorkspaceFileForSearch({ owner: f.owner, ...projectClaim }, signal) + await indexWorkspaceFileForSearch( + { workspaceId: f.workspaceId, ...(await claim(workspaceFile.id)) }, + signal + ) + expect((await search(f, 'owner-isolated')).results.map((hit) => hit.fileId)).toEqual([ + projectFile.file.id, + ]) + expect((await search(f, 'workspaceneedle')).results).toEqual([]) + const legacy = await searchWorkspaceFileContent.execute({ + principal: f.reader, + input: { + workspaceId: f.workspaceId, + query: 'owner-isolated', + mode: 'exact', + maxResults: 100, + }, + }) + expect(legacy.results.map((hit) => hit.fileId)).toEqual([workspaceFile.id]) + }) + + check( + 'fences an old Project build and dispatch token after a content revision changes', + async () => { + const f = await fixture() + const created = await create(f, 'supersededneedle') + const claimed = await claim(created.file.id) + const revision = { + owner: f.owner, + fileId: created.file.id, + sourceContentUpdatedAt: new Date(claimed.sourceContentUpdatedAt), + } + const build = await beginFileSearchBuild(revision, claimed.dispatchToken) + if (!build) throw new Error('Current Project build was not admitted') + await appendFileSearchChunks( + build, + [{ ordinal: 0, lineStart: 1, fragment: false, overlap: 0, content: 'supersededneedle' }], + signal + ) + await changeFile(created.file.id, 'currentneedle') + expect( + await publishFileSearchBuild( + build, + { status: 'ready', lineCount: 1, indexedBytes: 16, chunkCount: 1 }, + signal + ) + ).toBe(false) + await indexWorkspaceFileForSearch({ owner: f.owner, ...claimed }, signal) + await indexWorkspaceFileForSearch( + { owner: f.owner, ...(await claim(created.file.id)) }, + signal + ) + expect((await search(f, 'supersededneedle')).results).toEqual([]) + expect((await search(f, 'currentneedle')).results.map((hit) => hit.fileId)).toEqual([ + created.file.id, + ]) + } + ) + + check('keeps empty and recursive folder scopes exact after the creator is detached', async () => { + const f = await fixture() + const folder = await createFolder(f, 'Architecture') + const child = await createFolder(f, 'Backend', folder.folder.id) + const created = await create(f, 'folderneedle', { folderId: child.folder.id }) + await db + .update(workspaceFiles) + .set({ userId: null }) + .where(eq(workspaceFiles.id, created.file.id)) + await indexWorkspaceFileForSearch({ owner: f.owner, ...(await claim(created.file.id)) }, signal) + expect((await search(f, 'folderneedle', { folderPaths: [] })).results).toEqual([]) + expect( + ( + await search(f, 'folderneedle', { + folderPaths: ['/Architecture'], + includeSubfolders: false, + }) + ).results + ).toEqual([]) + expect( + (await search(f, 'folderneedle', { folderPaths: ['/Architecture'] })).results.map( + (hit) => hit.fileId + ) + ).toEqual([created.file.id]) + }) + + check('rejects forged owner payloads without changing the current revision', async () => { + const f = await fixture() + const created = await create(f, 'boundedneedle') + const claimed = await claim(created.file.id) + await expect( + indexWorkspaceFileForSearch( + { owner: { entityType: 'organization', entityId: f.organizationId }, ...claimed }, + signal + ) + ).rejects.toThrow() + await expect( + indexWorkspaceFileForSearch( + { owner: f.owner, workspaceId: f.workspaceId, ...claimed }, + signal + ) + ).rejects.toThrow() + const [revision] = await db + .select() + .from(workspaceFileSearchRevision) + .where(eq(workspaceFileSearchRevision.fileId, created.file.id)) + expect(revision.status).toBe('pending') + expect(revision.buildId).toBeNull() + }) + + check( + 'reads only an owner and dependency-bound cached artifact without executing source', + async () => { + const f = await fixture() + const dependency = await create(f, 'artifact dependency', { name: 'input.txt' }) + const source = `getFileBase64('${dependency.file.id}')` + const generated = await create(f, source, { name: 'report.docx' }) + const [dependencyRow] = await db + .select() + .from(workspaceFiles) + .where(eq(workspaceFiles.id, dependency.file.id)) + const artifact = Buffer.from('PK\u0003\u0004cached office bytes') + await storeCompiledDoc( + f.owner, + source, + 'docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + artifact, + fileDocumentInputIdentity(f.owner, [dependencyRow]) + ) + const { loadFileSearchSource } = await import('@/lib/workspace-files/search/source') + const revision = { + owner: f.owner, + fileId: generated.file.id, + sourceContentUpdatedAt: generated.file.contentUpdatedAt, + } + const loaded = await loadFileSearchSource(revision, signal) + expect(loaded?.bytes.buffer).toEqual(artifact) + expect(loaded?.bytes.kind).toBe('artifact') + expect(loaded?.dependencies.map((row) => row.fileId)).toEqual([dependency.file.id]) + await changeFile(dependency.file.id, 'new input') + const changed = await loadFileSearchSource(revision, signal) + expect(changed?.bytes.kind).toBe('source') + expect(changed?.bytes.buffer.toString()).toBe(source) + } + ) + + check( + 'refuses publication after a generated input changes without a parent source edit', + async () => { + const f = await fixture() + const dependency = await create(f, 'before', { name: 'dependency.md' }) + const created = await create(f, 'generated source', { name: 'derived.docx' }) + const claimed = await claim(created.file.id) + const build = await beginFileSearchBuild( + { + owner: f.owner, + fileId: created.file.id, + sourceContentUpdatedAt: new Date(claimed.sourceContentUpdatedAt), + }, + claimed.dispatchToken + ) + if (!build) throw new Error('Current Project build was not admitted') + await appendFileSearchChunks( + build, + [{ ordinal: 0, lineStart: 1, fragment: false, overlap: 0, content: 'old dependency text' }], + signal + ) + const dependencies = [ + { + fileId: dependency.file.id, + key: dependency.file.key, + sourceContentUpdatedAt: dependency.file.contentUpdatedAt, + }, + ] + await changeFile(dependency.file.id, 'after') + expect( + await publishFileSearchBuild( + build, + { status: 'ready', lineCount: 1, indexedBytes: 19, chunkCount: 1, dependencies }, + signal + ) + ).toBe(false) + expect((await search(f, 'old dependency')).results).toEqual([]) + } + ) + + check('retains workspace generated-input provenance in indexed snippet delivery', async () => { + const f = await fixture() + const ciphertext = 'fixture-encrypted-generated-input' + const dependency = await uploadWorkspaceFile( + f.workspaceId, + f.ownerId, + Buffer.from('input'), + 'secret-input.txt', + 'text/plain', + { + notifyWorkspaceChange: false, + secretProvenance: { + status: 'exact', + entries: [ + { + encryptedValue: ciphertext, + sourceUserId: f.ownerId, + sourceWorkspaceId: f.workspaceId, + }, + ], + }, + } + ) + const source = `getFileBase64('${dependency.id}')` + const generated = await uploadWorkspaceFile( + f.workspaceId, + f.ownerId, + Buffer.from(source), + 'derived.docx', + 'text/plain', + { notifyWorkspaceChange: false, secretProvenance: { status: 'exact', entries: [] } } + ) + const [dependencyRow] = await db + .select() + .from(workspaceFiles) + .where(eq(workspaceFiles.id, dependency.id)) + const owner = { entityType: 'workspace' as const, entityId: f.workspaceId } + const artifact = await Packer.toBuffer( + new Document({ sections: [{ children: [new Paragraph('derivedsnippetneedle')] }] }) + ) + await storeCompiledDoc( + owner, + source, + 'docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + artifact, + fileDocumentInputIdentity(owner, [dependencyRow]) + ) + await indexWorkspaceFileForSearch({ owner, ...(await claim(generated.id)) }, signal) + const found = await searchWorkspaceFileContent.execute({ + principal: f.principal, + input: { + workspaceId: f.workspaceId, + query: 'derivedsnippetneedle', + mode: 'exact', + maxResults: 100, + }, + }) + expect(found.results.map((hit) => hit.fileId)).toEqual([generated.id]) + const provenance = await getFileContentProvenance( + f.principal, + f.workspaceId, + found.sources, + signal + ) + expect(provenance.complete).toBe(true) + expect(provenance.entries).toContainEqual({ encryptedValue: ciphertext }) + }) + + check( + 'ignores stale artifact completion without invalidating a newer ready revision', + async () => { + const f = await fixture() + const dependency = await create(f, 'before', { name: 'input.txt' }) + const source = `getFileBase64('${dependency.file.id}')` + const generated = await create(f, source, { name: 'race.docx' }) + const [dependencyRow] = await db + .select() + .from(workspaceFiles) + .where(eq(workspaceFiles.id, dependency.file.id)) + const artifactKeys: string[] = [] + await storeCompiledDoc( + f.owner, + source, + 'docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + await Packer.toBuffer( + new Document({ sections: [{ children: [new Paragraph('oldrender')] }] }) + ), + fileDocumentInputIdentity(f.owner, [dependencyRow]), + (key) => artifactKeys.push(key) + ) + const artifactKey = artifactKeys.find((key) => key.endsWith('.docx')) + if (!artifactKey) throw new Error('Artifact was not stored') + const completion = { + owner: f.owner, + file: { + fileId: generated.file.id, + key: generated.file.key, + contentUpdatedAt: generated.file.contentUpdatedAt, + }, + dependencies: [ + { + fileId: dependency.file.id, + key: dependency.file.key, + sourceContentUpdatedAt: dependency.file.contentUpdatedAt, + }, + ], + artifactKey, + } + await changeFile(generated.file.id, 'newrevisionneedle') + await indexWorkspaceFileForSearch( + { owner: f.owner, ...(await claim(generated.file.id)) }, + signal + ) + const [ready] = await db + .select() + .from(workspaceFileSearchRevision) + .where(eq(workspaceFileSearchRevision.fileId, generated.file.id)) + const { markFileSearchArtifactReadyInTx } = await import( + '@/lib/workspace-files/search/artifact-ready' + ) + expect(await db.transaction((tx) => markFileSearchArtifactReadyInTx(tx, completion))).toBe( + false + ) + const [unchanged] = await db + .select() + .from(workspaceFileSearchRevision) + .where(eq(workspaceFileSearchRevision.fileId, generated.file.id)) + expect(unchanged.status).toBe('ready') + expect(unchanged.buildId).toBe(ready.buildId) + expect((await search(f, 'newrevisionneedle')).count).toBe(1) + } + ) + check( + 'fences an artifact completion queued behind a concurrent dependency revision', + async () => { + const f = await fixture() + const dependency = await create(f, 'before', { name: 'concurrent-input.txt' }) + const source = `getFileBase64('${dependency.file.id}')` + const generated = await create(f, source, { name: 'concurrent.docx' }) + await indexWorkspaceFileForSearch( + { owner: f.owner, ...(await claim(generated.file.id)) }, + signal + ) + const [ready] = await db + .select() + .from(workspaceFileSearchRevision) + .where(eq(workspaceFileSearchRevision.fileId, generated.file.id)) + const [dependencyRow] = await db + .select() + .from(workspaceFiles) + .where(eq(workspaceFiles.id, dependency.file.id)) + const artifactKeys: string[] = [] + await storeCompiledDoc( + f.owner, + source, + 'docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + await Packer.toBuffer( + new Document({ sections: [{ children: [new Paragraph('old-contributor')] }] }) + ), + fileDocumentInputIdentity(f.owner, [dependencyRow]), + (key) => artifactKeys.push(key) + ) + const artifactKey = artifactKeys.find((key) => key.endsWith('.docx')) + if (!artifactKey) throw new Error('Artifact was not stored') + const { markFileSearchArtifactReadyInTx } = await import( + '@/lib/workspace-files/search/artifact-ready' + ) + let completion: Promise | undefined + let completionPid: number | undefined + let completionError: unknown + await db.transaction(async (tx) => { + await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where(eq(workspaceFiles.id, dependency.file.id)) + .for('update') + completion = runOutsideTransactionContext(() => + db.transaction(async (pendingTx) => { + const [backend] = await pendingTx.execute<{ pid: number }>( + sql`SELECT pg_backend_pid() AS pid` + ) + completionPid = backend.pid + return markFileSearchArtifactReadyInTx(pendingTx, { + owner: f.owner, + file: { + fileId: generated.file.id, + key: generated.file.key, + contentUpdatedAt: generated.file.contentUpdatedAt, + }, + dependencies: [ + { + fileId: dependency.file.id, + key: dependency.file.key, + sourceContentUpdatedAt: dependency.file.contentUpdatedAt, + }, + ], + artifactKey, + }) + }) + ) + void completion.catch((error: unknown) => { + completionError = error + }) + let waited = false + const deadline = Date.now() + 2_000 + while (Date.now() < deadline) { + if (completionError) throw completionError + if (completionPid !== undefined) { + const [activity] = await tx.execute<{ waiting: boolean }>( + sql`SELECT wait_event_type = 'Lock' AS waiting FROM pg_stat_activity WHERE pid = ${completionPid}` + ) + if (activity?.waiting) { + waited = true + break + } + } + await sleep(10) + } + expect(waited).toBe(true) + await tx + .update(workspaceFiles) + .set({ contentUpdatedAt: new Date(dependency.file.contentUpdatedAt.getTime() + 1_000) }) + .where(eq(workspaceFiles.id, dependency.file.id)) + }) + expect(await completion).toBe(false) + const [unchanged] = await db + .select() + .from(workspaceFileSearchRevision) + .where(eq(workspaceFileSearchRevision.fileId, generated.file.id)) + expect(unchanged.buildId).toBe(ready.buildId) + expect((await search(f, source)).results).toEqual([]) + } + ) + check('waits at Project authority before taking file locks for a background build', async () => { + const f = await fixture() + const generated = await create(f, 'authority-order-needle') + const claimed = await claim(generated.file.id) + let building: ReturnType | undefined + try { + await db.transaction(async (tx) => { + const [backend] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + await lockProject(tx, f.projectId) + await tx + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where(eq(workspaceFiles.id, generated.file.id)) + .for('share') + building = runOutsideTransactionContext(() => + beginFileSearchBuild( + { + owner: f.owner, + fileId: generated.file.id, + sourceContentUpdatedAt: new Date(claimed.sourceContentUpdatedAt), + }, + claimed.dispatchToken + ) + ) + void building.catch(() => {}) + let event: string | undefined + const deadline = Date.now() + 2_000 + while (Date.now() < deadline) { + const [blocked] = await tx.execute<{ + event: string + }>(sql`SELECT wait_event AS event FROM pg_stat_activity + WHERE ${backend.pid} = ANY(pg_blocking_pids(pid)) AND wait_event_type = 'Lock'`) + if (blocked) { + event = blocked.event + break + } + await sleep(10) + } + expect(event).toBe('advisory') + }) + } finally { + await building + } + }) +}) + +afterAll(async () => { + const report = process.env.FILE_OWNER_SEARCH_REPORT_PATH + if (report) { + await mkdir(dirname(report), { recursive: true }) + await writeFile(report, JSON.stringify({ checks }, null, 2)) + } + const fileIds = ( + await db + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where( + inArray( + workspaceFiles.entityId, + fixtures.flatMap((f) => [f.projectId, f.workspaceId]) + ) + ) + ).map((row) => row.id) + if (fileIds.length) { + const builds = await db + .select({ id: workspaceFileSearchBuild.id }) + .from(workspaceFileSearchBuild) + .where(inArray(workspaceFileSearchBuild.fileId, fileIds)) + await db.delete(workspaceFiles).where(inArray(workspaceFiles.id, fileIds)) + if (builds.length) { + await db.delete(workspaceFileSearchChunk).where( + inArray( + workspaceFileSearchChunk.buildId, + builds.map((row) => row.id) + ) + ) + await db.delete(workspaceFileSearchBuild).where( + inArray( + workspaceFileSearchBuild.id, + builds.map((row) => row.id) + ) + ) + } + } + for (const f of fixtures) { + await db.delete(folder).where(eq(folder.entityId, f.projectId)) + await deleteWorkspaceFixture(db, eq(workspace.id, f.workspaceId)) + await db.delete(project).where(eq(project.id, f.projectId)) + await db.delete(organization).where(eq(organization.id, f.organizationId)) + await db.delete(user).where(inArray(user.id, [f.ownerId, f.readerId])) + } + await rm(storageRoot, { recursive: true, force: true }) + await db.$client.end() +}) diff --git a/apps/sim/lib/workspace-files/search/repository.ts b/apps/sim/lib/workspace-files/search/repository.ts index 2c97222ca28..0029a55cee8 100644 --- a/apps/sim/lib/workspace-files/search/repository.ts +++ b/apps/sim/lib/workspace-files/search/repository.ts @@ -1,10 +1,11 @@ import { dbFor } from '@sim/db' -import { workspaceFileSearchRevision, workspaceFiles } from '@sim/db/schema' +import { fileSearchDependency, workspaceFileSearchRevision, workspaceFiles } from '@sim/db/schema' import { getPostgresErrorCode } from '@sim/utils/errors' import { and, eq, inArray, isNull, or, type SQL, type SQLWrapper, sql } from 'drizzle-orm' import type { DbTransaction } from '@/lib/db/types' import type { FolderIdScope } from '@/lib/folders/scope' import type { WorkspaceFileSecretProvenanceIdentity } from '@/lib/uploads/contexts/workspace/workspace-file-secret-provenance' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' import { fileSearchAdmission } from '@/lib/workspace-files/search/admission' import { probeFileSearchCandidates, @@ -27,6 +28,12 @@ import { type FileSearchMatchRange, FileSearchPatternError, } from '@/lib/workspace-files/search/pattern' +import { + currentFileSearchDependencies, + resolveFileSearchOwner, + searchableFileCondition, +} from '@/lib/workspace-files/search/scope' +import type { FileSearchDependencyIdentity } from '@/lib/workspace-files/search/source' import { buildLiteralMatchStart, buildMatchExpression, @@ -44,7 +51,9 @@ export interface WorkspaceFileSearchIndexStatus { export interface WorkspaceFileSearchSource { identity: WorkspaceFileSecretProvenanceIdentity - ownerUserId: string + ownerUserId?: string + buildId: string + dependencies: (FileSearchDependencyIdentity & { ownerUserId?: string })[] } export interface WorkspaceFileSearchResult { @@ -60,8 +69,17 @@ export interface WorkspaceFileSearchResult { sources: WorkspaceFileSearchSource[] } -interface SearchWorkspaceFileIndexInput { - workspaceId: string +interface FileSearchSource extends Omit { + owner: EditableFileOwner + identity: { fileId: string; key: string; contentUpdatedAt: Date } +} + +export interface FileSearchResult extends Omit { + sources: FileSearchSource[] +} + +interface SearchFileIndexInput { + owner: EditableFileOwner pattern: CompiledFileSearchPattern maxResults: number /** Restricts the search to one folder scope. Absent searches the workspace. */ @@ -204,13 +222,14 @@ async function readCandidateLines( return lines.map((line) => ({ ...candidates[line.candidate], ...line })) } -export async function searchWorkspaceFileIndex({ - workspaceId, +export async function searchFileIndex({ + owner, pattern, maxResults, folderScope, signal, -}: SearchWorkspaceFileIndexInput): Promise { +}: SearchFileIndexInput): Promise { + owner = resolveFileSearchOwner({ owner }) signal?.throwIfAborted() if (!Number.isInteger(maxResults) || maxResults < 1 || maxResults > FILE_SEARCH_MAX_RESULTS) { throw new FileSearchPatternError( @@ -228,12 +247,12 @@ export async function searchWorkspaceFileIndex({ const folderPredicate = folderScope ? buildFolderPredicate(folderScope) : undefined return fileSearchAdmission.run( - workspaceId, + `${owner.entityType}:${owner.entityId}`, async (signal, deadlineAt) => { try { signal.throwIfAborted() /** Metadata pages and line reads share one deadline and a consistent revision snapshot. */ - const { rows, coverageRows } = await dbFor('search').transaction( + const { rows, coverageRows, dependencies } = await dbFor('search').transaction( async (tx) => { signal?.throwIfAborted() const deadline = Math.min(deadlineAt, Date.now() + FILE_SEARCH_STATEMENT_TIMEOUT_MS) @@ -247,7 +266,7 @@ export async function searchWorkspaceFileIndex({ /** Transaction-owned slots release on completion, cancellation, or connection loss. */ for (const [scope, capacity] of [ - [`workspace:${workspaceId}`, FILE_SEARCH_QUERY_WORKSPACE_CONCURRENCY], + [`${owner.entityType}:${owner.entityId}`, FILE_SEARCH_QUERY_WORKSPACE_CONCURRENCY], ['global', FILE_SEARCH_QUERY_GLOBAL_CONCURRENCY], ] as const) { const slots = @@ -273,7 +292,7 @@ export async function searchWorkspaceFileIndex({ await guardRemainingTime() /** Probe without a global sort. Rare queries finish here; broad queries scan files in order. */ const probed = await probeFileSearchCandidates(tx, { - workspaceId, + owner, pattern, folderPredicate, }) @@ -286,7 +305,7 @@ export async function searchWorkspaceFileIndex({ if (broad) { candidates = await readOrderedFileSearchCandidates( tx, - { workspaceId, pattern, folderPredicate }, + { owner, pattern, folderPredicate }, after ) } @@ -329,8 +348,8 @@ export async function searchWorkspaceFileIndex({ signal?.throwIfAborted() const coverage = await tx .select({ - readyFiles: sql`count(*) filter (where ${workspaceFileSearchRevision.status} = 'ready' AND ${workspaceFileSearchRevision.buildId} IS NOT NULL)::int`, - pendingFiles: sql`count(*) filter (where ${workspaceFileSearchRevision.status} is null or ${workspaceFileSearchRevision.status} = 'pending' or (${workspaceFileSearchRevision.status} = 'ready' AND ${workspaceFileSearchRevision.buildId} IS NULL))::int`, + readyFiles: sql`count(*) filter (where ${workspaceFileSearchRevision.status} = 'ready' AND ${workspaceFileSearchRevision.buildId} IS NOT NULL AND ${currentFileSearchDependencies(workspaceFileSearchRevision.buildId, owner)})::int`, + pendingFiles: sql`count(*) filter (where ${workspaceFileSearchRevision.status} is null or ${workspaceFileSearchRevision.status} = 'pending' or (${workspaceFileSearchRevision.status} = 'ready' AND (${workspaceFileSearchRevision.buildId} IS NULL OR NOT (${currentFileSearchDependencies(workspaceFileSearchRevision.buildId, owner)}))))::int`, failedFiles: sql`count(*) filter (where ${workspaceFileSearchRevision.status} = 'failed')::int`, skippedFiles: sql`count(*) filter (where ${workspaceFileSearchRevision.status} = 'skipped')::int`, partialFiles: sql`0::int`, @@ -348,14 +367,36 @@ export async function searchWorkspaceFileIndex({ ) .where( and( - eq(workspaceFiles.workspaceId, workspaceId), - eq(workspaceFiles.context, 'workspace'), + searchableFileCondition(owner), isNull(workspaceFiles.deletedAt), folderPredicate ) ) - return { rows: matchedRows, coverageRows: coverage } + const buildIds = [ + ...new Set(matchedRows.slice(0, maxResults).map((row) => row.buildId)), + ] + const dependencies = + buildIds.length === 0 + ? [] + : await tx + .select({ + buildId: fileSearchDependency.buildId, + fileId: fileSearchDependency.fileId, + key: fileSearchDependency.key, + sourceContentUpdatedAt: fileSearchDependency.sourceContentUpdatedAt, + ownerUserId: workspaceFiles.userId, + }) + .from(fileSearchDependency) + .innerJoin( + workspaceFiles, + and( + eq(workspaceFiles.id, fileSearchDependency.fileId), + searchableFileCondition(owner) + ) + ) + .where(inArray(fileSearchDependency.buildId, buildIds)) + return { rows: matchedRows, coverageRows: coverage, dependencies } }, { isolationLevel: 'repeatable read', accessMode: 'read only' } ) @@ -369,16 +410,23 @@ export async function searchWorkspaceFileIndex({ skippedFiles: 0, partialFiles: 0, } - const sourcesByFileId = new Map() + const sourcesByFileId = new Map() for (const row of resultRows) { sourcesByFileId.set(row.fileId, { + owner, identity: { fileId: row.fileId, key: row.fileKey, - context: 'workspace', contentUpdatedAt: row.contentUpdatedAt, }, - ownerUserId: row.ownerUserId, + ...(row.ownerUserId ? { ownerUserId: row.ownerUserId } : {}), + buildId: row.buildId, + dependencies: dependencies + .filter((entry) => entry.buildId === row.buildId) + .map(({ buildId: _buildId, ownerUserId, ...entry }) => ({ + ...entry, + ...(ownerUserId ? { ownerUserId } : {}), + })), }) } @@ -413,3 +461,43 @@ export async function searchWorkspaceFileIndex({ signal ) } + +/** Existing workspace callers retain their scope and result while sharing the owner-aware index. */ +export async function searchWorkspaceFileIndex( + input: Omit & { workspaceId: string } +): Promise { + const { workspaceId, ...query } = input + const result = await searchFileIndex({ + ...query, + owner: { entityType: 'workspace', entityId: workspaceId }, + }) + return toWorkspaceFileSearchResult(result) +} + +/** Preserves the legacy private classification list, including every contributing input. */ +export function toWorkspaceFileSearchResult(result: FileSearchResult): WorkspaceFileSearchResult { + const sources = new Map() + for (const source of result.sources) { + if (source.owner.entityType !== 'workspace') + throw new Error('Workspace search result has a different owner') + sources.set(source.identity.fileId, { + ...source, + identity: { ...source.identity, context: 'workspace' }, + }) + for (const dependency of source.dependencies) { + if (sources.has(dependency.fileId)) continue + sources.set(dependency.fileId, { + identity: { + fileId: dependency.fileId, + key: dependency.key, + contentUpdatedAt: dependency.sourceContentUpdatedAt, + context: 'workspace', + }, + ...(dependency.ownerUserId ? { ownerUserId: dependency.ownerUserId } : {}), + buildId: source.buildId, + dependencies: [], + }) + } + } + return { ...result, sources: [...sources.values()] } +} diff --git a/apps/sim/lib/workspace-files/search/scope.ts b/apps/sim/lib/workspace-files/search/scope.ts new file mode 100644 index 00000000000..cdda9caf7e3 --- /dev/null +++ b/apps/sim/lib/workspace-files/search/scope.ts @@ -0,0 +1,76 @@ +import { workspaceFiles } from '@sim/db/schema' +import { isRecordLike } from '@sim/utils/object' +import { and, eq, type SQL, type SQLWrapper, sql } from 'drizzle-orm' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' +import { fileOwnerCondition } from '@/lib/workspace-files/ownership-query' + +export type FileSearchOwnerScope = + | { owner: EditableFileOwner; workspaceId?: never } + | { owner?: never; workspaceId: string } + +/** Old workspace jobs decode explicitly; future or conflicting scopes never fall back. */ +export function resolveFileSearchOwner(input: unknown): EditableFileOwner { + if (!isRecordLike(input)) throw new Error('Invalid file search owner') + const owner = input.owner + if (owner === undefined) { + if ( + typeof input.workspaceId === 'string' && + input.workspaceId.trim() === input.workspaceId && + input.workspaceId.length > 0 && + input.workspaceId.length <= 200 + ) + return { entityType: 'workspace', entityId: input.workspaceId } + } else if ( + input.workspaceId === undefined && + isRecordLike(owner) && + (owner.entityType === 'workspace' || owner.entityType === 'project') && + typeof owner.entityId === 'string' && + owner.entityId.trim() === owner.entityId && + owner.entityId.length > 0 && + owner.entityId.length <= 200 + ) + return { entityType: owner.entityType, entityId: owner.entityId } + throw new Error('Invalid file search owner') +} + +export function fileSearchOwnerFields(owner: EditableFileOwner) { + return { + entityType: owner.entityType, + entityId: owner.entityId, + workspaceId: owner.entityType === 'workspace' ? owner.entityId : null, + } +} + +interface SearchOwnerColumns { + workspaceId: SQLWrapper + entityType: SQLWrapper + entityId: SQLWrapper +} + +/** Workspace predicates retain the deployed GIN index while Project predicates use the new one. */ +export function fileSearchOwnerCondition(table: SearchOwnerColumns, owner: EditableFileOwner): SQL { + return owner.entityType === 'workspace' + ? sql`${table.workspaceId} = ${owner.entityId} AND (${table.entityType} IS NULL OR (${table.entityType} = 'workspace' AND ${table.entityId} = ${owner.entityId}))` + : sql`${table.entityType} = 'project' AND ${table.entityId} = ${owner.entityId} AND ${table.workspaceId} IS NULL` +} + +export function searchableFileCondition(owner: EditableFileOwner): SQL { + const result = and(fileOwnerCondition(owner), eq(workspaceFiles.context, owner.entityType)) + if (!result) throw new OrchestrationError('not_found', 'File owner not found') + return result +} + +/** Input deletion leaves manifest rows intact, so absence can never become valid empty provenance. */ +export function currentFileSearchDependencies(buildId: SQLWrapper, owner: EditableFileOwner): SQL { + return sql`NOT EXISTS ( + SELECT 1 FROM file_search_dependency dependency + LEFT JOIN workspace_files input ON input.id = dependency.file_id + AND input.context = ${owner.entityType} + AND coalesce(input.entity_type, 'workspace') = ${owner.entityType} + AND coalesce(input.entity_id, input.workspace_id) = ${owner.entityId} + WHERE dependency.build_id = ${buildId} + AND (input.id IS NULL OR input.deleted_at IS NOT NULL OR input.key <> dependency.key + OR input.content_updated_at <> dependency.source_content_updated_at) + )` +} diff --git a/apps/sim/lib/workspace-files/search/source.ts b/apps/sim/lib/workspace-files/search/source.ts new file mode 100644 index 00000000000..dfda0873bf3 --- /dev/null +++ b/apps/sim/lib/workspace-files/search/source.ts @@ -0,0 +1,111 @@ +import { db } from '@sim/db' +import type { WorkspaceFileRow } from '@sim/db/schema' +import { workspaceFiles } from '@sim/db/schema' +import { and, asc, eq, inArray, isNull } from 'drizzle-orm' +import { assertKnownSizeWithinLimit } from '@/lib/core/utils/stream-limits' +import { downloadFile } from '@/lib/uploads/core/storage-service' +import { + collectReferencedFileIds, + getE2BDocFormat, + isCompiledDocumentBuffer, +} from '@/lib/uploads/documents' +import { compiledArtifactKey, loadCompiledDoc } from '@/lib/uploads/documents/compiled-store' +import { fileDocumentInputIdentity } from '@/lib/uploads/documents/input-identity' +import { + FILE_SEARCH_MAX_DEPENDENCIES, + FILE_SEARCH_MAX_SOURCE_BYTES, +} from '@/lib/workspace-files/search/constants' +import type { IndexableBytes } from '@/lib/workspace-files/search/extract' +import { FileSearchExclusionError } from '@/lib/workspace-files/search/index-plan' +import type { FileSearchRevision } from '@/lib/workspace-files/search/index-state' +import { resolveFileSearchOwner, searchableFileCondition } from '@/lib/workspace-files/search/scope' + +export interface FileSearchDependencyIdentity { + fileId: string + key: string + sourceContentUpdatedAt: Date +} + +/** System indexers read current stored inputs and caches; they never execute generation source. */ +export async function loadFileSearchSource( + revision: FileSearchRevision, + signal: AbortSignal +): Promise<{ + file: WorkspaceFileRow + bytes: IndexableBytes + dependencies: FileSearchDependencyIdentity[] + artifactKey?: string +} | null> { + const owner = resolveFileSearchOwner(revision) + signal.throwIfAborted() + const [file] = await db + .select() + .from(workspaceFiles) + .where( + and( + searchableFileCondition(owner), + eq(workspaceFiles.id, revision.fileId), + isNull(workspaceFiles.deletedAt), + eq(workspaceFiles.contentUpdatedAt, revision.sourceContentUpdatedAt) + ) + ) + .limit(1) + if (!file) return null + if (file.sizeBytes !== null) + assertKnownSizeWithinLimit(file.sizeBytes, FILE_SEARCH_MAX_SOURCE_BYTES, 'search source') + const raw = await downloadFile({ + key: file.key, + context: owner.entityType, + maxBytes: FILE_SEARCH_MAX_SOURCE_BYTES, + signal, + }) + const format = await getE2BDocFormat(file.originalName) + if (!format || isCompiledDocumentBuffer(file.originalName, raw)) + return { file, bytes: { buffer: raw, kind: 'stored' }, dependencies: [] } + const source = raw.toString('utf8') + const ids = [...new Set(collectReferencedFileIds(source))] + if (ids.length > FILE_SEARCH_MAX_DEPENDENCIES) + throw new FileSearchExclusionError('incomplete_extraction') + const inputs = + ids.length === 0 + ? [] + : await db + .select() + .from(workspaceFiles) + .where( + and( + searchableFileCondition(owner), + inArray(workspaceFiles.id, ids), + isNull(workspaceFiles.deletedAt) + ) + ) + .orderBy(asc(workspaceFiles.id)) + if (inputs.length !== ids.length) throw new FileSearchExclusionError('incomplete_extraction') + signal.throwIfAborted() + const artifact = await loadCompiledDoc( + owner, + source, + format.ext, + fileDocumentInputIdentity(owner, inputs), + { maxBytes: FILE_SEARCH_MAX_SOURCE_BYTES, signal } + ) + return { + file, + bytes: artifact ? { buffer: artifact, kind: 'artifact' } : { buffer: raw, kind: 'source' }, + ...(artifact + ? { + artifactKey: compiledArtifactKey( + owner, + source, + format.ext, + fileDocumentInputIdentity(owner, inputs) + ), + } + : {}), + dependencies: inputs.map((input) => ({ + fileId: input.id, + key: input.key, + sourceContentUpdatedAt: input.contentUpdatedAt, + })), + } +} diff --git a/apps/sim/lib/workspaces/admin-move.test.ts b/apps/sim/lib/workspaces/admin-move.test.ts index 958893aabb6..cb68c5f27dd 100644 --- a/apps/sim/lib/workspaces/admin-move.test.ts +++ b/apps/sim/lib/workspaces/admin-move.test.ts @@ -9,6 +9,10 @@ import { } from '@sim/db/schema' import { dbChainMockFns, queueTableRows, resetDbChainMock } from '@sim/testing' import { auditMock, auditMockFns } from '@sim/testing/mocks/audit.mock' +import { + billingPayerTransferMock, + billingPayerTransferMockFns, +} from '@sim/testing/mocks/billing-payer-transfer.mock' import { customBlockOperationsMock, customBlockOperationsMockFns, @@ -49,7 +53,6 @@ const { findUnpublishableCustomBlocks, findSourceOrgCustomBlocksForWorkspace, cleanupSourceOrganizationArtifactsTx, - changeWorkspaceStoragePayerInTx, acquireInvitationMutationLocks, countPendingSeatInvitations, resolveSeatCapacity, @@ -69,7 +72,6 @@ const { cleanupSourceOrganizationArtifactsTx: vi.fn(() => Promise.resolve({ detachedPermissionGroupIds: [] }) ), - changeWorkspaceStoragePayerInTx: vi.fn(), acquireInvitationMutationLocks: vi.fn(), countPendingSeatInvitations: vi.fn(() => Promise.resolve(0)), resolveSeatCapacity: vi.fn(() => Promise.resolve(10)), @@ -77,6 +79,10 @@ const { getSourceOrganization: vi.fn(), })) +const { + mockChangeProjectAndWorkspaceStoragePayersInTx: changeProjectAndWorkspaceStoragePayersInTx, +} = billingPayerTransferMockFns + const { mockRecordAudit: recordAudit, mockRecordAuditOnce: recordAuditOnce } = auditMockFns const { mockAcquireOrganizationMutationLock: acquireOrganizationMutationLock } = organizationMembershipMockFns @@ -123,7 +129,7 @@ const POPULATED_CREDENTIALS = { vi.mock('@sim/audit', () => auditMock) vi.mock('@/lib/billing/organizations/membership', () => organizationMembershipMock) -vi.mock('@/lib/billing/storage/payer-transfer', () => ({ changeWorkspaceStoragePayerInTx })) +vi.mock('@/lib/billing/storage/payer-transfer', () => billingPayerTransferMock) vi.mock('@/lib/billing/validation/seat-management', () => ({ countPendingSeatInvitations, planHasFixedSeatCap: vi.fn((plan: string) => plan === 'enterprise'), @@ -252,7 +258,7 @@ beforeEach(() => { }) collectWorkspaceCredentialSummary.mockResolvedValue(EMPTY_CREDENTIALS) getSourceOrganization.mockResolvedValue(SOURCE_ORGANIZATION) - changeWorkspaceStoragePayerInTx.mockResolvedValue({ + changeProjectAndWorkspaceStoragePayersInTx.mockResolvedValue({ billableBytes: 128, newPayer: { type: 'organization', id: destination.id }, oldPayer: { type: 'user', id: personalWorkspace.billedAccountUserId }, @@ -491,7 +497,7 @@ describe('moveWorkspaceToOrganization retries', () => { expect(invalidateWorkspaceTableLimitsCache).not.toHaveBeenCalled() expect(dbChainMockFns.insert).not.toHaveBeenCalled() expect(dbChainMockFns.update).not.toHaveBeenCalled() - expect(changeWorkspaceStoragePayerInTx).not.toHaveBeenCalled() + expect(changeProjectAndWorkspaceStoragePayersInTx).not.toHaveBeenCalled() }) it('persists a standalone operation marker atomically with a new move', async () => { @@ -566,7 +572,7 @@ describe('moveWorkspaceToOrganization retries', () => { }) ).rejects.toMatchObject>({ code: 'seat-capacity-exceeded' }) - expect(changeWorkspaceStoragePayerInTx).not.toHaveBeenCalled() + expect(changeProjectAndWorkspaceStoragePayersInTx).not.toHaveBeenCalled() }) it('does not let a new operation ID claim a workspace moved by another operation', async () => { @@ -642,14 +648,18 @@ describe('moveWorkspaceToOrganization retries', () => { durableOperationId: 'operation-1', }) - expect(changeWorkspaceStoragePayerInTx).toHaveBeenCalledWith( + expect(changeProjectAndWorkspaceStoragePayersInTx).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ - organizationId: destination.id, - expectedCurrentPayer: { - organizationId: 'org-source', - billedAccountUserId: organizationWorkspace.billedAccountUserId, - }, + workspaceChanges: [ + expect.objectContaining({ + organizationId: destination.id, + expectedCurrentPayer: { + organizationId: 'org-source', + billedAccountUserId: organizationWorkspace.billedAccountUserId, + }, + }), + ], }) ) }) @@ -660,7 +670,7 @@ describe('moveWorkspaceToOrganization retries', () => { * organizations get locked. When the workspace moves between that read and * the locked read, the attempt must abort and retry — otherwise the payer * transfer is fenced on an organization the workspace has already left, and - * `changeWorkspaceStoragePayerInTx`'s optimistic check is the only thing + * `changeProjectAndWorkspaceStoragePayersInTx`'s optimistic check is the only thing * standing between that and a corrupted storage ledger. * * First locked read reports a different organization than the pre-read, so @@ -680,11 +690,15 @@ describe('moveWorkspaceToOrganization retries', () => { durableOperationId: 'operation-1', }) - expect(changeWorkspaceStoragePayerInTx).toHaveBeenCalledTimes(1) - expect(changeWorkspaceStoragePayerInTx).toHaveBeenCalledWith( + expect(changeProjectAndWorkspaceStoragePayersInTx).toHaveBeenCalledTimes(1) + expect(changeProjectAndWorkspaceStoragePayersInTx).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ - expectedCurrentPayer: expect.objectContaining({ organizationId: 'org-moved' }), + workspaceChanges: [ + expect.objectContaining({ + expectedCurrentPayer: expect.objectContaining({ organizationId: 'org-moved' }), + }), + ], }) ) }) @@ -729,7 +743,7 @@ describe('moveWorkspaceToOrganization retries', () => { }) ).rejects.toMatchObject>({ code: 'fork-lineage-conflict' }) - expect(changeWorkspaceStoragePayerInTx).not.toHaveBeenCalled() + expect(changeProjectAndWorkspaceStoragePayersInTx).not.toHaveBeenCalled() expect(deleteCustomBlock).not.toHaveBeenCalled() }) @@ -752,7 +766,7 @@ describe('moveWorkspaceToOrganization retries', () => { }) ).rejects.toThrow(/without a durable operation id/) - expect(changeWorkspaceStoragePayerInTx).not.toHaveBeenCalled() + expect(changeProjectAndWorkspaceStoragePayersInTx).not.toHaveBeenCalled() }) it('refuses an entitlement downgrade without mutating anything', async () => { @@ -778,7 +792,7 @@ describe('moveWorkspaceToOrganization retries', () => { code: 'destination-entitlement-downgrade', }) - expect(changeWorkspaceStoragePayerInTx).not.toHaveBeenCalled() + expect(changeProjectAndWorkspaceStoragePayersInTx).not.toHaveBeenCalled() }) it('rejects a stale batch selection when workspace ownership changed', async () => { @@ -795,7 +809,7 @@ describe('moveWorkspaceToOrganization retries', () => { code: 'workspace-owner-changed', }) - expect(changeWorkspaceStoragePayerInTx).not.toHaveBeenCalled() + expect(changeProjectAndWorkspaceStoragePayersInTx).not.toHaveBeenCalled() expect(dbChainMockFns.update).not.toHaveBeenCalled() }) }) diff --git a/apps/sim/lib/workspaces/admin-move.ts b/apps/sim/lib/workspaces/admin-move.ts index 9ad81035fd1..836cbd8f51e 100644 --- a/apps/sim/lib/workspaces/admin-move.ts +++ b/apps/sim/lib/workspaces/admin-move.ts @@ -20,7 +20,6 @@ import { normalizeEmail } from '@sim/utils/string' import { and, asc, count, eq, gt, ilike, inArray, isNotNull, lte, ne, or, sql } from 'drizzle-orm' import { alias } from 'drizzle-orm/pg-core' import { acquireOrganizationMutationLock } from '@/lib/billing/organizations/membership' -import { changeWorkspaceStoragePayerInTx } from '@/lib/billing/storage/payer-transfer' import { ENTITLED_SUBSCRIPTION_STATUSES, hasPaidSubscriptionStatus, @@ -1359,17 +1358,23 @@ export async function moveWorkspaceToOrganization(params: { }) : { detachedPermissionGroupIds: [] } - await transferWorkspaceProjects(tx, [params.workspaceId], params.destinationOrganizationId) - - await changeWorkspaceStoragePayerInTx(tx, { - workspaceId: params.workspaceId, - organizationId: params.destinationOrganizationId, - billedAccountUserId: destination.ownerId, - expectedCurrentPayer: { - organizationId: workspaceRow.organizationId, - billedAccountUserId: workspaceRow.billedAccountUserId, - }, - }) + await transferWorkspaceProjects( + tx, + [params.workspaceId], + params.destinationOrganizationId, + undefined, + [ + { + workspaceId: params.workspaceId, + organizationId: params.destinationOrganizationId, + billedAccountUserId: destination.ownerId, + expectedCurrentPayer: { + organizationId: workspaceRow.organizationId, + billedAccountUserId: workspaceRow.billedAccountUserId, + }, + }, + ] + ) await tx .update(workspace) diff --git a/apps/sim/lib/workspaces/organization-workspaces.test.ts b/apps/sim/lib/workspaces/organization-workspaces.test.ts index eb919215c51..33e0a1eaaf6 100644 --- a/apps/sim/lib/workspaces/organization-workspaces.test.ts +++ b/apps/sim/lib/workspaces/organization-workspaces.test.ts @@ -1,6 +1,10 @@ import { db } from '@sim/db' import { dbChainMockFns, queueTableRows, resetDbChainMock, schemaMock } from '@sim/testing' import { auditMock } from '@sim/testing/mocks/audit.mock' +import { + billingPayerTransferMock, + billingPayerTransferMockFns, +} from '@sim/testing/mocks/billing-payer-transfer.mock' import { billingUsageMock, billingUsageMockFns } from '@sim/testing/mocks/billing-usage.mock' import { idMock, idMockFns } from '@sim/testing/mocks/id.mock' import { @@ -10,18 +14,14 @@ import { import { tableBillingMock } from '@sim/testing/mocks/table-billing.mock' import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' -const { mockAcquireInvitationMutationLocks, mockChangeWorkspaceStoragePayersInTx } = vi.hoisted( - () => ({ - mockAcquireInvitationMutationLocks: vi.fn(), - mockChangeWorkspaceStoragePayersInTx: vi.fn(), - }) -) +const { mockAcquireInvitationMutationLocks } = vi.hoisted(() => ({ + mockAcquireInvitationMutationLocks: vi.fn(), +})) +const { mockChangeProjectAndWorkspaceStoragePayersInTx } = billingPayerTransferMockFns vi.mock('@/lib/billing/organizations/membership', () => organizationMembershipMock) -vi.mock('@/lib/billing/storage/payer-transfer', () => ({ - changeWorkspaceStoragePayersInTx: mockChangeWorkspaceStoragePayersInTx, -})) +vi.mock('@/lib/billing/storage/payer-transfer', () => billingPayerTransferMock) vi.mock('@/lib/invitations/locks', () => ({ acquireInvitationMutationLocks: mockAcquireInvitationMutationLocks, @@ -55,7 +55,7 @@ describe('organization workspace helpers', () => { beforeEach(() => { resetDbChainMock() mockEnsureUserInOrganizationTx.mockReset() - mockChangeWorkspaceStoragePayersInTx.mockReset() + mockChangeProjectAndWorkspaceStoragePayersInTx.mockReset() mockSyncUsageLimitsFromSubscription.mockResolvedValue(undefined) mockReapplyPaidOrgJoinBillingForExistingMemberTx.mockResolvedValue({ proUsageSnapshotted: false, @@ -182,7 +182,7 @@ describe('organization workspace helpers', () => { }) it.each(['standalone', 'enlisted'] as const)( - 'detaches with invitation locks before the organization fence and workspace rows in a %s transaction', + 'detaches with invitation locks before the organization fence and payer transfer in a %s transaction', async (mode) => { queueTableRows(schemaMock.workspace, [{ id: 'ws-1' }]) queueTableRows(schemaMock.invitation, [{ id: 'invite-pending' }, { id: 'invite-terminal' }]) @@ -220,24 +220,27 @@ describe('organization workspace helpers', () => { expect(mockAcquireOrganizationMutationLock.mock.invocationCallOrder[0]).toBeLessThan( dbChainMockFns.select.mock.invocationCallOrder[2] ) + expect(mockChangeProjectAndWorkspaceStoragePayersInTx).toHaveBeenCalledTimes(1) expect(mockAcquireOrganizationMutationLock.mock.invocationCallOrder[0]).toBeLessThan( - dbChainMockFns.for.mock.invocationCallOrder[0] - ) - expect(mockChangeWorkspaceStoragePayersInTx).toHaveBeenCalledTimes(1) - expect(dbChainMockFns.for.mock.invocationCallOrder[0]).toBeLessThan( - mockChangeWorkspaceStoragePayersInTx.mock.invocationCallOrder[0] + mockChangeProjectAndWorkspaceStoragePayersInTx.mock.invocationCallOrder[0] ) - expect(mockChangeWorkspaceStoragePayersInTx).toHaveBeenCalledWith(expect.anything(), [ + expect(mockChangeProjectAndWorkspaceStoragePayersInTx).toHaveBeenCalledWith( + expect.anything(), { - workspaceId: 'ws-1', - organizationId: null, - billedAccountUserId: 'owner-1', - expectedCurrentPayer: { - organizationId: 'org-1', - billedAccountUserId: 'old-owner', - }, - }, - ]) + projectChanges: [], + workspaceChanges: [ + { + workspaceId: 'ws-1', + organizationId: null, + billedAccountUserId: 'owner-1', + expectedCurrentPayer: { + organizationId: 'org-1', + billedAccountUserId: 'old-owner', + }, + }, + ], + } + ) expect(dbChainMockFns.set).toHaveBeenCalledWith( expect.objectContaining({ workspaceMode: 'grandfathered_shared', @@ -280,7 +283,7 @@ describe('organization workspace helpers', () => { } ) expect(dbChainMockFns.for).not.toHaveBeenCalled() - expect(mockChangeWorkspaceStoragePayersInTx).not.toHaveBeenCalled() + expect(mockChangeProjectAndWorkspaceStoragePayersInTx).not.toHaveBeenCalled() expect(dbChainMockFns.update).not.toHaveBeenCalled() expect(dbChainMockFns.insert).not.toHaveBeenCalled() } @@ -314,7 +317,7 @@ describe('organization workspace helpers', () => { } ) expect(dbChainMockFns.for).not.toHaveBeenCalled() - expect(mockChangeWorkspaceStoragePayersInTx).not.toHaveBeenCalled() + expect(mockChangeProjectAndWorkspaceStoragePayersInTx).not.toHaveBeenCalled() expect(dbChainMockFns.update).not.toHaveBeenCalled() expect(dbChainMockFns.insert).not.toHaveBeenCalled() } @@ -337,8 +340,9 @@ describe('organization workspace helpers', () => { invitationIds: ['invite-1', 'invite-2'], workspaceIds: ['ws-1', 'ws-2'], }) - expect(mockChangeWorkspaceStoragePayersInTx).toHaveBeenCalledWith(expect.anything(), [ - expect.objectContaining({ workspaceId: 'ws-2' }), - ]) + expect(mockChangeProjectAndWorkspaceStoragePayersInTx).toHaveBeenCalledWith(expect.anything(), { + projectChanges: [], + workspaceChanges: [expect.objectContaining({ workspaceId: 'ws-2' })], + }) }) }) diff --git a/apps/sim/lib/workspaces/organization-workspaces.ts b/apps/sim/lib/workspaces/organization-workspaces.ts index 2db7b69675d..7e7de57b341 100644 --- a/apps/sim/lib/workspaces/organization-workspaces.ts +++ b/apps/sim/lib/workspaces/organization-workspaces.ts @@ -10,7 +10,6 @@ import { ensureUserInOrganizationTx, reapplyPaidOrgJoinBillingForExistingMemberTx, } from '@/lib/billing/organizations/membership' -import { changeWorkspaceStoragePayersInTx } from '@/lib/billing/storage/payer-transfer' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { acquireInvitationMutationLocks } from '@/lib/invitations/locks' @@ -352,10 +351,12 @@ export async function attachOwnedWorkspacesToOrganizationTx( } } - await transferWorkspaceProjects(tx, ownedWorkspaceIds, organizationId) const now = new Date() - await changeWorkspaceStoragePayersInTx( + await transferWorkspaceProjects( tx, + ownedWorkspaceIds, + organizationId, + undefined, ownedWorkspaces.map((ownedWorkspace) => ({ workspaceId: ownedWorkspace.id, organizationId, @@ -494,8 +495,6 @@ export async function detachOrganizationWorkspacesTx( const workspaceIds = organizationWorkspaces .map((organizationWorkspace) => organizationWorkspace.id) .sort() - await transferWorkspaceProjects(tx, workspaceIds, null, organizationOwnerId ?? undefined) - await lockWorkspaceRowsForPayerChanges(tx, workspaceIds) const payerChanges = organizationWorkspaces.map((organizationWorkspace) => ({ workspaceId: organizationWorkspace.id, organizationId: null, @@ -506,7 +505,13 @@ export async function detachOrganizationWorkspacesTx( }, })) - await changeWorkspaceStoragePayersInTx(tx, payerChanges) + await transferWorkspaceProjects( + tx, + workspaceIds, + null, + organizationOwnerId ?? undefined, + payerChanges + ) if (workspaceIds.length === 0) return [] diff --git a/apps/sim/lib/workspaces/utils.test.ts b/apps/sim/lib/workspaces/utils.test.ts index 27f92803b89..3a01fb7cc74 100644 --- a/apps/sim/lib/workspaces/utils.test.ts +++ b/apps/sim/lib/workspaces/utils.test.ts @@ -1,13 +1,12 @@ import { dbChainMockFns, resetDbChainMock } from '@sim/testing' +import { + billingPayerTransferMock, + billingPayerTransferMockFns, +} from '@sim/testing/mocks/billing-payer-transfer.mock' import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' -const { mockChangeWorkspaceStoragePayerInTx } = vi.hoisted(() => ({ - mockChangeWorkspaceStoragePayerInTx: vi.fn(), -})) - -vi.mock('@/lib/billing/storage/payer-transfer', () => ({ - changeWorkspaceStoragePayerInTx: mockChangeWorkspaceStoragePayerInTx, -})) +const { mockChangeWorkspaceStoragePayerInTx } = billingPayerTransferMockFns +vi.mock('@/lib/billing/storage/payer-transfer', () => billingPayerTransferMock) import { listAccessibleWorkspaceRowsForUser, diff --git a/apps/sim/lib/workspaces/utils.ts b/apps/sim/lib/workspaces/utils.ts index 40dcd050f33..6a3e04c5e39 100644 --- a/apps/sim/lib/workspaces/utils.ts +++ b/apps/sim/lib/workspaces/utils.ts @@ -4,8 +4,11 @@ import { createLogger } from '@sim/logger' import type { PermissionType } from '@sim/platform-authz/workspace' import { isOrgAdminRole } from '@sim/platform-authz/workspace' import { generateId } from '@sim/utils/id' -import { and, count, desc, eq, inArray, isNull, ne, sql } from 'drizzle-orm' -import { changeWorkspaceStoragePayerInTx } from '@/lib/billing/storage/payer-transfer' +import { and, count, desc, eq, inArray, isNull, ne, notInArray, sql } from 'drizzle-orm' +import { + type ChangeWorkspaceStoragePayerParams, + changeWorkspaceStoragePayerInTx, +} from '@/lib/billing/storage/payer-transfer' import type { DbOrTx } from '@/lib/db/types' const logger = createLogger('WorkspaceUtils') @@ -359,42 +362,36 @@ export async function reassignWorkflowOwnershipForWorkspaceMemberRemovalTx({ return { reassigned, unresolved } } -/** - * Reassigns `billedAccountUserId` on every workspace that points to `departingUserId` to - * another eligible user, so the user can be deleted without violating the `workspace.billed_account_user_id` - * foreign key (`ON DELETE NO ACTION`). - * - * Preference order for the replacement: - * 1. The workspace owner (if different from the departing user) - * 2. Any existing workspace admin - * - * Returns the list of workspaces that could not be reassigned (no owner + no admin). Callers should - * block user deletion when `unresolved.length > 0` so we never leave an orphaned billing reference. - * - * Pass `executor` to enroll the handover in a caller-owned transaction — account - * deletion does, so a later failure in the same transaction rolls the transfers - * back instead of leaving a workspace reassigned for a deletion that never - * happened. The per-workspace `transaction` call below becomes a savepoint in - * that case, preserving the payer-ledger atomicity it exists for. - */ -export async function reassignBilledAccountForUser( +/** Plans the existing owner/admin replacement policy before a compound payer transfer takes locks. */ +export async function planBilledAccountReassignmentsForUser( departingUserId: string, - executor: DbOrTx = db -): Promise { - const billedWorkspaces = await executor + executor: DbOrTx, + options: { excludeWorkspaceIds?: string[]; lockRows?: boolean } = {} +): Promise<{ changes: ChangeWorkspaceStoragePayerParams[]; unresolved: string[] }> { + const query = executor .select({ id: workspaceTable.id, ownerId: workspaceTable.ownerId, organizationId: workspaceTable.organizationId, }) .from(workspaceTable) - .where(eq(workspaceTable.billedAccountUserId, departingUserId)) + .where( + and( + eq(workspaceTable.billedAccountUserId, departingUserId), + options.excludeWorkspaceIds?.length + ? notInArray(workspaceTable.id, options.excludeWorkspaceIds) + : undefined + ) + ) + const billedWorkspaces = options.lockRows + ? await query.orderBy(workspaceTable.id).for('no key update') + : await query if (billedWorkspaces.length === 0) { - return { reassigned: [], unresolved: [] } + return { changes: [], unresolved: [] } } - const reassigned: ReassignBilledAccountResult['reassigned'] = [] + const changes: ChangeWorkspaceStoragePayerParams[] = [] const unresolved: string[] = [] for (const ws of billedWorkspaces) { @@ -422,23 +419,58 @@ export async function reassignBilledAccountForUser( continue } - await executor.transaction(async (tx) => { - await changeWorkspaceStoragePayerInTx(tx, { - workspaceId: ws.id, + changes.push({ + workspaceId: ws.id, + organizationId: ws.organizationId, + billedAccountUserId: replacement, + expectedCurrentPayer: { organizationId: ws.organizationId, - billedAccountUserId: replacement, - expectedCurrentPayer: { - organizationId: ws.organizationId, - billedAccountUserId: departingUserId, - }, - }) + billedAccountUserId: departingUserId, + }, + }) + } + return { changes, unresolved } +} + +/** + * Reassigns `billedAccountUserId` on every workspace that points to `departingUserId` to + * another eligible user, so the user can be deleted without violating the `workspace.billed_account_user_id` + * foreign key (`ON DELETE NO ACTION`). + * + * Preference order for the replacement: + * 1. The workspace owner (if different from the departing user) + * 2. Any existing workspace admin + * + * Returns the list of workspaces that could not be reassigned (no owner + no admin). Callers should + * block user deletion when `unresolved.length > 0` so we never leave an orphaned billing reference. + * + * Pass `executor` to enroll the handover in a caller-owned transaction — account + * deletion does, so a later failure in the same transaction rolls the transfers + * back instead of leaving a workspace reassigned for a deletion that never + * happened. The per-workspace `transaction` call below becomes a savepoint in + * that case, preserving the payer-ledger atomicity it exists for. + */ +export async function reassignBilledAccountForUser( + departingUserId: string, + executor: DbOrTx = db +): Promise { + const { changes, unresolved } = await planBilledAccountReassignmentsForUser( + departingUserId, + executor + ) + const reassigned: ReassignBilledAccountResult['reassigned'] = [] + for (const change of changes) { + await executor.transaction(async (tx) => { + await changeWorkspaceStoragePayerInTx(tx, change) await tx .update(workspaceTable) .set({ updatedAt: new Date() }) - .where(eq(workspaceTable.id, ws.id)) + .where(eq(workspaceTable.id, change.workspaceId)) + }) + reassigned.push({ + workspaceId: change.workspaceId, + newBilledAccountUserId: change.billedAccountUserId, }) - - reassigned.push({ workspaceId: ws.id, newBilledAccountUserId: replacement }) } if (reassigned.length > 0) { diff --git a/apps/sim/sandbox-tasks/pptx-generate.ts b/apps/sim/sandbox-tasks/pptx-generate.ts index 706876d3e12..24eaa7e885b 100644 --- a/apps/sim/sandbox-tasks/pptx-generate.ts +++ b/apps/sim/sandbox-tasks/pptx-generate.ts @@ -2,7 +2,7 @@ import { MAX_SANDBOX_IMAGE_DATA_URI_CHARS } from '@/lib/execution/isolated-vm-li import { workspaceFileBroker } from '@/lib/execution/sandbox/brokers/workspace-file' import { defineSandboxTask } from '@/lib/execution/sandbox/define-task' import type { SandboxTaskInput } from '@/lib/execution/sandbox/types' -import { PPTX_SHIM_JS } from '@/lib/mothership/tools/server/files/pptx-shim' +import { PPTX_SHIM_JS } from '@/lib/uploads/documents/pptx-shim' export const pptxGenerateTask = defineSandboxTask({ id: 'pptx-generate', diff --git a/knip.jsonc b/knip.jsonc index ea731f3397f..225a488a254 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -44,6 +44,7 @@ "lib/mothership/generated/**": ["exports", "types", "duplicates"], // API conventions require exported named wire schemas and aliases, including before client adoption. "lib/api/contracts/projects.ts": ["exports", "types"], + "lib/api/contracts/v2/uploads.ts": ["types"], "sandbox-tasks/index.ts": ["files"], "components/mcp/index.ts": ["files"], "triggers/quickbooks/index.ts": ["files"], @@ -71,7 +72,14 @@ // Live test spawns this path with cwd=MSHIP_WORKER_ROOT (a sibling repo). "ignoreUnresolved": ["tools/probes/src/controller-run-read.ts"] }, - "apps/realtime": { "entry": ["src/bootstrap.ts"] }, + "apps/realtime": { + "entry": [ + "src/bootstrap.ts", + // Integration mode discovers these suites and setup by filename. + "src/**/*.integration.ts", + "vitest.integration.setup.ts" + ] + }, "apps/desktop": { // scripts/build.ts and e2e/updater.spec.ts supply these to esbuild by path. "entry": [ diff --git a/packages/auth/src/principal.ts b/packages/auth/src/principal.ts index 7f0f7cb1ce7..57d2e9ed549 100644 --- a/packages/auth/src/principal.ts +++ b/packages/auth/src/principal.ts @@ -188,6 +188,8 @@ export type BoundWorkflowExecutionDelegatedPrincipal = WorkflowExecutionDelegate export type DelegatedPrincipal = SubjectDelegatedPrincipal | WorkflowExecutionDelegatedPrincipal /** Search-only authority delegated by a current organization member. */ +export type ResourceEntityType = 'workspace' | 'project' | 'organization' | 'user' + interface OrganizationDelegatedPrincipalBase { kind: 'organization_delegated' organizationId: string diff --git a/packages/db/file-creator-lifetime.integration.ts b/packages/db/file-creator-lifetime.integration.ts new file mode 100644 index 00000000000..75c3467a67f --- /dev/null +++ b/packages/db/file-creator-lifetime.integration.ts @@ -0,0 +1,349 @@ +import { readFileSync } from 'node:fs' +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname } from 'node:path' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { createDeferred } from '@sim/testing/helpers/deferred' +import { getErrorMessage } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import postgres, { type Sql } from 'postgres' +import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' + +const databaseUrl = readTestDatabaseUrl() +const migrations = [ + '0396_file_entity_ownership.sql', + '0397_file_folder_version_ownership.sql', + '0398_file_creator_lifetime.sql', +].map((name) => readFileSync(new URL(`./migrations/${name}`, import.meta.url), 'utf8')) +const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = + [] +function check(name: string, run: () => Promise) { + it(name, async () => { + const started = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - started }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - started, + error: getErrorMessage(error), + }) + throw error + } + }) +} + +describe('Project creator lifetime in PostgreSQL', () => { + const schemaName = `file_creator_${generateId().replaceAll('-', '')}` + let sql: Sql + let admin: Sql + async function applyMigration(migration: string) { + for (const statement of migration.split('--> statement-breakpoint')) { + if (statement.trim()) await sql.unsafe(statement) + } + } + async function waitForDatabaseLock(pid: number) { + for (let attempt = 0; attempt < 100; attempt += 1) { + const [state] = await sql<{ waiting: boolean }[]>`SELECT EXISTS ( + SELECT 1 FROM pg_stat_activity WHERE pid = ${pid} AND wait_event_type = 'Lock' + ) AS waiting` + if (state.waiting) return + await sleep(10) + } + throw new Error('Creator operation did not wait for its database lock') + } + beforeAll(async () => { + admin = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) + await admin.unsafe(`CREATE SCHEMA "${schemaName}"`) + sql = postgres(databaseUrl, { + max: 3, + onnotice: () => undefined, + connection: { search_path: schemaName, TimeZone: 'UTC' }, + }) + await sql`CREATE TABLE "user" (id text PRIMARY KEY)` + await sql`CREATE TABLE workspace (id text PRIMARY KEY)` + await sql`CREATE TABLE organization (id text PRIMARY KEY)` + await sql`CREATE TABLE project ( + id text PRIMARY KEY, name text NOT NULL DEFAULT 'Project', + owner_id text NOT NULL DEFAULT 'user-b' REFERENCES "user"(id) ON DELETE RESTRICT, + updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456' + )` + await sql`CREATE TABLE folder ( + id text PRIMARY KEY, name text NOT NULL, + user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, + workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE, + resource_type text NOT NULL DEFAULT 'workflow', + parent_id text REFERENCES folder(id) ON DELETE SET NULL, + deleted_at timestamp + )` + await sql`CREATE UNIQUE INDEX folder_workspace_resource_parent_name_active_unique + ON folder(workspace_id, resource_type, coalesce(parent_id, ''), name) + WHERE deleted_at IS NULL` + await sql`CREATE TABLE workspace_files ( + id text PRIMARY KEY, context text NOT NULL, + user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, + workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, + organization_id text REFERENCES organization(id) ON DELETE CASCADE, + folder_id text REFERENCES folder(id) ON DELETE SET NULL, + chat_id text, original_name text NOT NULL DEFAULT 'document.md', + key text NOT NULL DEFAULT 'unchanged-object-key', deleted_at timestamp, + content_updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456', + secret_provenance_version integer DEFAULT 1 + )` + await sql`CREATE UNIQUE INDEX workspace_files_workspace_folder_name_active_unique + ON workspace_files(workspace_id, coalesce(folder_id, ''), original_name) + WHERE deleted_at IS NULL AND context = 'workspace'` + await sql`CREATE TABLE workspace_file_version ( + id text PRIMARY KEY, file_id text NOT NULL REFERENCES workspace_files(id) ON DELETE CASCADE, + workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE, + version integer NOT NULL DEFAULT 1, key text NOT NULL DEFAULT 'unchanged-version-key', + author_user_ids text[] NOT NULL DEFAULT '{}'::text[], UNIQUE(file_id, version) + )` + for (const migration of migrations) await applyMigration(migration) + }) + + beforeEach(async () => { + await sql`TRUNCATE workspace_file_version, workspace_files, folder, project, workspace, organization, "user"` + await sql`INSERT INTO "user" VALUES ('user-a'), ('user-b')` + await sql`INSERT INTO workspace VALUES ('workspace-a'), ('workspace-b'), ('same-id')` + await sql`INSERT INTO organization VALUES ('organization-a')` + await sql`INSERT INTO project (id) VALUES ('project-a'), ('project-b'), ('same-id')` + }) + + afterAll(async () => { + try { + await sql?.end() + await admin?.unsafe(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`) + } finally { + await admin?.end() + const reportPath = process.env.FILE_CREATOR_LIFETIME_REPORT_PATH + if (reportPath) { + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) + } + } + }) + + check( + 'creator deletion preserves Project folders, heads, history and immutable attribution', + async () => { + await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES ('folder', 'Docs', 'user-a', 'file', 'project', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) + VALUES ('file', 'project', 'user-a', 'project', 'project-a', 'folder')` + await sql`INSERT INTO workspace_file_version (id, file_id, author_user_ids) + VALUES ('version', 'file', ARRAY['user-a'])` + const [before] = + await sql`SELECT key, content_updated_at, secret_provenance_version FROM workspace_files` + await sql`DELETE FROM "user" WHERE id = 'user-a'` + expect( + await sql`SELECT user_id, original_creator_user_id, entity_id FROM workspace_files` + ).toEqual([{ user_id: null, original_creator_user_id: 'user-a', entity_id: 'project-a' }]) + expect(await sql`SELECT user_id, original_creator_user_id, entity_id FROM folder`).toEqual([ + { user_id: null, original_creator_user_id: 'user-a', entity_id: 'project-a' }, + ]) + expect( + ( + await sql`SELECT key, content_updated_at, secret_provenance_version FROM workspace_files` + )[0] + ).toEqual(before) + expect(await sql`SELECT key, author_user_ids FROM workspace_file_version`).toEqual([ + { key: 'unchanged-version-key', author_user_ids: ['user-a'] }, + ]) + } + ) + + check( + 'legacy workspace and personal creator cascades still remove only their own rows', + async () => { + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + VALUES ('folder', 'Docs', 'user-a', 'workspace-a', 'file')` + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, folder_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a', 'folder'), + ('personal', 'copilot', 'user-a', NULL, NULL)` + await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` + await sql`DELETE FROM "user" WHERE id = 'user-a'` + expect(await sql`SELECT id FROM workspace_files`).toHaveLength(0) + expect(await sql`SELECT id FROM folder`).toHaveLength(0) + expect(await sql`SELECT id FROM workspace_file_version`).toHaveLength(0) + expect(await sql`SELECT id FROM workspace`).toHaveLength(3) + } + ) + + check( + 'backfills legacy Project attribution in bounded pages without changing live creator or content', + async () => { + await sql`ALTER TABLE workspace_files DISABLE TRIGGER workspace_files_creator_lifetime` + await sql`ALTER TABLE folder DISABLE TRIGGER folder_creator_lifetime` + try { + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, original_name) + SELECT 'file-' || lpad(id::text, 4, '0'), 'project', 'user-a', 'project', 'project-a', 'document-' || id + FROM generate_series(1, 507) id` + await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + SELECT 'folder-' || lpad(id::text, 4, '0'), 'Docs ' || id, 'user-a', 'file', 'project', 'project-a' + FROM generate_series(1, 507) id` + } finally { + await sql`ALTER TABLE workspace_files ENABLE TRIGGER workspace_files_creator_lifetime` + await sql`ALTER TABLE folder ENABLE TRIGGER folder_creator_lifetime` + } + const { backfillProjectFileCreators } = await import( + '@sim/db/script-migrations/0032_backfill_project_file_creators' + ) + expect(await backfillProjectFileCreators(sql)).toEqual({ files: 507, folders: 507 }) + expect(await backfillProjectFileCreators(sql)).toEqual({ files: 0, folders: 0 }) + expect( + await sql`SELECT DISTINCT user_id, original_creator_user_id, key, secret_provenance_version FROM workspace_files` + ).toEqual([ + { + user_id: 'user-a', + original_creator_user_id: 'user-a', + key: 'unchanged-object-key', + secret_provenance_version: 1, + }, + ]) + await sql`DELETE FROM "user" WHERE id = 'user-a'` + expect( + ( + await sql`SELECT count(*)::integer AS count FROM workspace_files WHERE user_id IS NULL AND original_creator_user_id = 'user-a'` + )[0].count + ).toBe(507) + expect( + ( + await sql`SELECT count(*)::integer AS count FROM folder WHERE user_id IS NULL AND original_creator_user_id = 'user-a'` + )[0].count + ).toBe(507) + } + ) + + check( + 'rejects missing and forged Project creators and immutable snapshot replacement', + async () => { + await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, original_creator_user_id) + VALUES ('missing', 'project', NULL, 'project', 'project-a', 'user-a')`).rejects.toMatchObject( + { code: '23514' } + ) + await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, original_creator_user_id) + VALUES ('forged', 'project', 'user-a', 'project', 'project-a', 'user-b')`).rejects.toMatchObject( + { code: '23514' } + ) + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + for (const creator of ['user-b', null]) { + await expect( + sql`UPDATE workspace_files SET original_creator_user_id = ${creator} WHERE id = 'file'` + ).rejects.toMatchObject({ code: '23514' }) + } + await expect( + sql`UPDATE workspace_files SET user_id = 'user-b' WHERE id = 'file'` + ).rejects.toMatchObject({ code: '23514' }) + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id, original_creator_user_id) + VALUES ('forged', 'Docs', 'user-a', 'file', 'project', 'project-a', 'user-b')`).rejects.toMatchObject( + { code: '23514' } + ) + await expect(sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('legacy-null', 'workspace', NULL, 'workspace-a')`).rejects.toMatchObject({ + code: '23514', + }) + } + ) + + check( + 'retained Project owner deletion stays fail-closed and rolls back creator cleanup', + async () => { + await sql`UPDATE project SET owner_id = 'user-a' WHERE id = 'project-a'` + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + await expect(sql`DELETE FROM "user" WHERE id = 'user-a'`).rejects.toMatchObject({ + code: '23503', + }) + expect((await sql`SELECT user_id, original_creator_user_id FROM workspace_files`)[0]).toEqual( + { user_id: 'user-a', original_creator_user_id: 'user-a' } + ) + await expect(sql`DELETE FROM project WHERE id = 'project-a'`).rejects.toMatchObject({ + code: '23503', + }) + } + ) + + for (const isolation of ['read committed', 'repeatable read'] as const) { + check( + `queued ${isolation} creator deletion preserves a committed concurrent Project file or retries`, + async () => { + const snapshot = createDeferred() + const startDelete = createDeferred() + const deletePid = createDeferred() + const inserted = createDeferred() + const releaseInsert = createDeferred() + const deletion = sql.begin(`isolation level ${isolation}`, async (tx) => { + const [connection] = await tx`SELECT pg_backend_pid() AS pid, count(*) FROM "user"` + deletePid.resolve(connection.pid) + snapshot.resolve() + await startDelete.promise + await tx`DELETE FROM "user" WHERE id = 'user-a'` + }) + const deletionOutcome = deletion.then( + () => ({ code: null }), + (error: unknown) => ({ code: (error as { code: string }).code }) + ) + await snapshot.promise + const insertion = sql.begin(async (tx) => { + await tx`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + inserted.resolve() + await releaseInsert.promise + }) + await Promise.race([insertion, inserted.promise]) + startDelete.resolve() + try { + await waitForDatabaseLock(await deletePid.promise) + } finally { + releaseInsert.resolve() + } + await insertion + const outcome = await deletionOutcome + expect(outcome.code).toBe(isolation === 'repeatable read' ? '40001' : null) + expect( + (await sql`SELECT user_id, original_creator_user_id FROM workspace_files`)[0] + ).toEqual({ + user_id: isolation === 'repeatable read' ? 'user-a' : null, + original_creator_user_id: 'user-a', + }) + } + ) + } + + check( + 'a Project insert queued behind creator deletion cannot resurrect a missing creator', + async () => { + const deleted = createDeferred() + const releaseDelete = createDeferred() + const insertPid = createDeferred() + const deletion = sql.begin(async (tx) => { + await tx`DELETE FROM "user" WHERE id = 'user-a'` + deleted.resolve() + await releaseDelete.promise + }) + await Promise.race([deletion, deleted.promise]) + const insertion = sql.begin(async (tx) => { + const [connection] = await tx`SELECT pg_backend_pid() AS pid` + insertPid.resolve(connection.pid) + await tx`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + }) + const insertionOutcome = insertion.then( + () => ({ code: null }), + (error: unknown) => ({ code: (error as { code: string }).code }) + ) + try { + await waitForDatabaseLock(await insertPid.promise) + } finally { + releaseDelete.resolve() + } + await deletion + expect((await insertionOutcome).code).toBe('23503') + expect(await sql`SELECT id FROM workspace_files`).toHaveLength(0) + } + ) +}) diff --git a/packages/db/file-entity-ownership.integration.ts b/packages/db/file-entity-ownership.integration.ts new file mode 100644 index 00000000000..be7339f03ae --- /dev/null +++ b/packages/db/file-entity-ownership.integration.ts @@ -0,0 +1,362 @@ +import { readFileSync } from 'node:fs' +import { backfillWorkspaceFileEntities } from '@sim/db/script-migrations/0030_backfill_workspace_file_entities' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { createDeferred } from '@sim/testing/helpers/deferred' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import postgres, { type Sql } from 'postgres' +import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' + +const databaseUrl = readTestDatabaseUrl() +const migration = readFileSync( + new URL('./migrations/0396_file_entity_ownership.sql', import.meta.url), + 'utf8' +) + +describe('file entity ownership migration in PostgreSQL', () => { + const schemaName = `file_entity_${generateId().replaceAll('-', '')}` + let sql: Sql + let admin: Sql + + async function applyMigration() { + for (const statement of migration.split('--> statement-breakpoint')) { + if (statement.trim()) await sql.unsafe(statement) + } + } + + async function waitForDatabaseLock(pid: number) { + for (let attempt = 0; attempt < 100; attempt += 1) { + const [state] = await sql<{ waiting: boolean }[]>` + SELECT EXISTS ( + SELECT 1 FROM pg_stat_activity WHERE pid = ${pid} AND wait_event_type = 'Lock' + ) AS waiting + ` + if (state.waiting) return + await sleep(10) + } + throw new Error('Concurrent ownership operation did not wait for its parent row lock') + } + + beforeAll(async () => { + admin = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) + await admin.unsafe(`CREATE SCHEMA "${schemaName}"`) + sql = postgres(databaseUrl, { + max: 3, + onnotice: () => undefined, + connection: { search_path: schemaName, TimeZone: 'UTC' }, + }) + await sql`CREATE TABLE "user" (id text PRIMARY KEY)` + await sql`CREATE TABLE workspace (id text PRIMARY KEY)` + await sql`CREATE TABLE organization (id text PRIMARY KEY)` + await sql`CREATE TABLE project ( + id text PRIMARY KEY, name text NOT NULL DEFAULT 'Project', + owner_id text NOT NULL DEFAULT 'user-b' REFERENCES "user"(id) ON DELETE RESTRICT, + organization_id text REFERENCES organization(id) ON DELETE RESTRICT, + archived_at timestamp, + updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456' + )` + await sql`CREATE TABLE workspace_files ( + id text PRIMARY KEY, + context text NOT NULL, + user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, + workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, + organization_id text REFERENCES organization(id) ON DELETE CASCADE, + folder_id text, + chat_id text, + key text NOT NULL DEFAULT 'unchanged-object-key', + deleted_at timestamp, + content_updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456', + secret_provenance_version integer DEFAULT 1 + )` + await applyMigration() + }) + + beforeEach(async () => { + await sql`TRUNCATE workspace_files, project, workspace, organization, "user"` + await sql`INSERT INTO "user" VALUES ('user-a'), ('user-b')` + await sql`INSERT INTO workspace VALUES ('workspace-a'), ('workspace-b')` + await sql`INSERT INTO organization VALUES ('organization-a')` + await sql`INSERT INTO project VALUES ('project-a'), ('project-b')` + }) + + afterAll(async () => { + try { + await sql?.end() + await admin?.unsafe(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`) + } finally { + await admin?.end() + } + }) + + it('backfills several bounded pages, preserves revisions, and reports unresolved legacy rows', async () => { + await sql`ALTER TABLE workspace_files DISABLE TRIGGER workspace_files_sync_entity_binding` + try { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + SELECT 'file-' || lpad(id::text, 4, '0'), 'workspace', 'user-a', 'workspace-a' + FROM generate_series(1, 506) id` + await sql`INSERT INTO workspace_files (id, context, user_id, organization_id) + VALUES ('org-kb', 'knowledge-base', 'user-a', 'organization-a')` + await sql`INSERT INTO workspace_files (id, context, user_id) + VALUES ('personal', 'copilot', 'user-a'), ('orphan-kb', 'knowledge-base', 'user-a'), + ('unknown', 'chat', 'user-a')` + } finally { + await sql`ALTER TABLE workspace_files ENABLE TRIGGER workspace_files_sync_entity_binding` + } + + const result = await backfillWorkspaceFileEntities(sql) + expect(result.backfilled).toBe(508) + expect(result.unresolved).toEqual([ + { context: 'knowledge-base', count: 1, sampleFileIds: ['orphan-kb'] }, + { context: 'chat', count: 1, sampleFileIds: ['unknown'] }, + ]) + const [changed] = await sql<{ count: number }[]>` + SELECT count(*)::integer AS count FROM workspace_files + WHERE key <> 'unchanged-object-key' + OR content_updated_at <> TIMESTAMP '2026-10-03 01:02:03.456' + OR secret_provenance_version <> 1 + ` + expect(changed.count).toBe(0) + const owners = await sql`SELECT id, entity_type, entity_id FROM workspace_files + WHERE id IN ('file-0001', 'org-kb', 'personal', 'orphan-kb') ORDER BY id` + expect(owners).toEqual([ + { id: 'file-0001', entity_type: 'workspace', entity_id: 'workspace-a' }, + { id: 'org-kb', entity_type: 'organization', entity_id: 'organization-a' }, + { id: 'orphan-kb', entity_type: null, entity_id: null }, + { id: 'personal', entity_type: 'user', entity_id: 'user-a' }, + ]) + expect((await backfillWorkspaceFileEntities(sql)).backfilled).toBe(0) + }) + + it('keeps legacy inserts and owner changes consistent while retaining chat purpose bindings', async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, chat_id) + VALUES ('chat-file', 'mothership', 'user-a', 'workspace-a', 'chat-a')` + await sql`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'chat-file'` + const [chat] = await sql`SELECT entity_type, entity_id, chat_id FROM workspace_files` + expect(chat).toEqual({ entity_type: 'workspace', entity_id: 'workspace-b', chat_id: 'chat-a' }) + await sql`UPDATE workspace_files SET context = 'workspace', chat_id = NULL WHERE id = 'chat-file'` + await sql`UPDATE workspace_files SET user_id = 'user-b' WHERE id = 'chat-file'` + const [materialized] = await sql`SELECT entity_type, entity_id, chat_id FROM workspace_files` + expect(materialized).toEqual({ + entity_type: 'workspace', + entity_id: 'workspace-b', + chat_id: null, + }) + await sql`INSERT INTO workspace_files (id, context, user_id) VALUES ('personal', 'copilot', 'user-a')` + await sql`UPDATE workspace_files SET user_id = 'user-b' WHERE id = 'personal'` + const [personal] = + await sql`SELECT entity_type, entity_id FROM workspace_files WHERE id = 'personal'` + expect(personal).toEqual({ entity_type: 'user', entity_id: 'user-b' }) + }) + + it.each(['execution', 'workspace-logos', 'knowledge-base'])( + 'maps legacy %s rows to their workspace', + async (context) => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('file', ${context}, 'user-a', 'workspace-a')` + const [row] = await sql`SELECT entity_type, entity_id FROM workspace_files WHERE id = 'file'` + expect(row).toEqual({ entity_type: 'workspace', entity_id: 'workspace-a' }) + } + ) + + it('refuses contradictory, incomplete, and unregistered explicit ownership without changing stored ownership', async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a')` + for (const pair of [ + { type: 'workspace', id: 'workspace-b' }, + { type: 'workspace', id: null }, + { type: null, id: 'workspace-a' }, + { type: 'user', id: 'user-a' }, + { type: 'future', id: 'workspace-a' }, + { type: 'workspace', id: '' }, + ]) { + await expect(sql`UPDATE workspace_files SET entity_type = ${pair.type}, entity_id = ${pair.id} + WHERE id = 'file'`).rejects.toMatchObject({ code: '23514' }) + } + const [row] = await sql`SELECT entity_type, entity_id FROM workspace_files WHERE id = 'file'` + expect(row).toEqual({ entity_type: 'workspace', entity_id: 'workspace-a' }) + await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('unknown', 'general', 'user-a', 'user', 'user-a')`).rejects.toMatchObject({ + code: '23514', + }) + }) + + it('requires a real Project and refuses legacy tenancy or unsupported folders on a Project file', async () => { + await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('missing', 'project', 'user-a', 'project', 'missing')`).rejects.toMatchObject({ + code: '23503', + }) + await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, workspace_id) + VALUES ('bound', 'project', 'user-a', 'project', 'project-a', 'workspace-a')`).rejects.toMatchObject( + { code: '23514' } + ) + await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) + VALUES ('folder', 'project', 'user-a', 'project', 'project-a', 'workspace-folder')`).rejects.toMatchObject( + { code: '23514' } + ) + await expect(sql`INSERT INTO workspace_files (id, context, user_id) + VALUES ('unowned', 'project', 'user-a')`).rejects.toMatchObject({ code: '23514' }) + }) + + it('retains Project ownership through attribution changes and blocks owner deletion while retained files exist', async () => { + const [projectBefore] = await sql`SELECT * FROM project WHERE id = 'project-a'` + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + const [projectAfter] = await sql`SELECT * FROM project WHERE id = 'project-a'` + expect(projectAfter).toEqual(projectBefore) + await sql`UPDATE workspace_files SET user_id = 'user-b', deleted_at = now() WHERE id = 'file'` + await expect( + sql`UPDATE workspace_files SET entity_type = NULL, entity_id = NULL WHERE id = 'file'` + ).rejects.toMatchObject({ code: '23514' }) + await expect( + sql`UPDATE workspace_files SET entity_id = 'project-b' WHERE id = 'file'` + ).rejects.toMatchObject({ code: '23514' }) + await expect(sql`DELETE FROM project WHERE id = 'project-a'`).rejects.toMatchObject({ + code: '23503', + }) + await expect( + sql`UPDATE project SET id = 'replacement' WHERE id = 'project-a'` + ).rejects.toMatchObject({ code: '23503' }) + await sql`DELETE FROM workspace_files WHERE id = 'file'` + await sql`DELETE FROM project WHERE id = 'project-a'` + expect(await sql`SELECT id FROM project WHERE id = 'project-a'`).toHaveLength(0) + }) + + it('protects shared Project files from uploader cascade until attribution has been reassigned', async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('shared-file', 'project', 'user-a', 'project', 'project-a')` + await expect(sql`DELETE FROM "user" WHERE id = 'user-a'`).rejects.toMatchObject({ + code: '23503', + }) + expect(await sql`SELECT id FROM workspace_files WHERE id = 'shared-file'`).toHaveLength(1) + await sql`UPDATE workspace_files SET user_id = 'user-b' WHERE id = 'shared-file'` + await sql`DELETE FROM "user" WHERE id = 'user-a'` + const [file] = + await sql`SELECT user_id, entity_type, entity_id FROM workspace_files WHERE id = 'shared-file'` + expect(file).toEqual({ user_id: 'user-b', entity_type: 'project', entity_id: 'project-a' }) + }) + + it('preserves legacy workspace, organization, and personal owner deletion behavior', async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('workspace-file', 'workspace', 'user-a', 'workspace-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, organization_id) + VALUES ('organization-file', 'knowledge-base', 'user-a', 'organization-a')` + await sql`INSERT INTO workspace_files (id, context, user_id) + VALUES ('personal-file', 'copilot', 'user-a')` + await sql`DELETE FROM workspace WHERE id = 'workspace-a'` + expect(await sql`SELECT id FROM workspace_files WHERE id = 'workspace-file'`).toHaveLength(0) + await sql`DELETE FROM organization WHERE id = 'organization-a'` + expect(await sql`SELECT id FROM workspace_files WHERE id = 'organization-file'`).toHaveLength(0) + await sql`DELETE FROM "user" WHERE id = 'user-a'` + expect(await sql`SELECT id FROM workspace_files`).toHaveLength(0) + }) + + it.each(['read committed', 'repeatable read'])( + 'serializes Project deletion behind an in-flight file insert at %s isolation', + async (isolation) => { + const inserted = createDeferred() + const release = createDeferred() + const creation = sql.begin(async (tx) => { + await tx`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + inserted.resolve() + await release.promise + }) + const deleting = createDeferred() + let rejection: Promise | undefined + try { + await inserted.promise + const deletion = sql.begin(`isolation level ${isolation}`, async (tx) => { + await tx`SET LOCAL statement_timeout = '5s'` + const [backend] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + deleting.resolve(backend.pid) + await tx`DELETE FROM project WHERE id = 'project-a'` + }) + rejection = expect(deletion).rejects.toMatchObject({ + code: isolation === 'repeatable read' ? '40001' : '23503', + }) + await waitForDatabaseLock(await deleting.promise) + } finally { + release.resolve() + await creation + } + await rejection + expect(await sql`SELECT id FROM project WHERE id = 'project-a'`).toHaveLength(1) + } + ) + + it.each(['read committed', 'repeatable read'])( + 'does not cascade a concurrently created Project file when its uploader is deleted at %s isolation', + async (isolation) => { + const inserted = createDeferred() + const release = createDeferred() + const creation = sql.begin(async (tx) => { + await tx`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + inserted.resolve() + await release.promise + }) + const deleting = createDeferred() + let rejection: Promise | undefined + try { + await inserted.promise + const deletion = sql.begin(`isolation level ${isolation}`, async (tx) => { + await tx`SET LOCAL statement_timeout = '5s'` + const [backend] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + deleting.resolve(backend.pid) + await tx`DELETE FROM "user" WHERE id = 'user-a'` + }) + rejection = expect(deletion).rejects.toMatchObject({ + code: isolation === 'repeatable read' ? '40001' : '23503', + }) + await waitForDatabaseLock(await deleting.promise) + } finally { + release.resolve() + await creation + } + await rejection + expect(await sql`SELECT id FROM workspace_files WHERE id = 'file'`).toHaveLength(1) + } + ) + + it.each(['read committed', 'repeatable read'])( + 'rejects a file insert when its Project is concurrently removed at %s isolation', + async (isolation) => { + const deleted = createDeferred() + const release = createDeferred() + const deletion = sql.begin(async (tx) => { + await tx`DELETE FROM project WHERE id = 'project-a'` + deleted.resolve() + await release.promise + }) + const inserting = createDeferred() + let rejection: Promise | undefined + try { + await deleted.promise + const creation = sql.begin(`isolation level ${isolation}`, async (tx) => { + await tx`SET LOCAL statement_timeout = '5s'` + const [backend] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + inserting.resolve(backend.pid) + await tx`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + }) + rejection = expect(creation).rejects.toMatchObject({ + code: isolation === 'repeatable read' ? '40001' : '23503', + }) + await waitForDatabaseLock(await inserting.promise) + } finally { + release.resolve() + await deletion + } + await rejection + expect(await sql`SELECT id FROM workspace_files WHERE id = 'file'`).toHaveLength(0) + } + ) + + it('can replay the additive SQL after files are present', async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a')` + await applyMigration() + const [row] = await sql`SELECT id, entity_type, entity_id FROM workspace_files` + expect(row).toEqual({ id: 'file', entity_type: 'workspace', entity_id: 'workspace-a' }) + }) +}) diff --git a/packages/db/file-folder-version-ownership.integration.ts b/packages/db/file-folder-version-ownership.integration.ts new file mode 100644 index 00000000000..5394cf556aa --- /dev/null +++ b/packages/db/file-folder-version-ownership.integration.ts @@ -0,0 +1,643 @@ +import { readFileSync } from 'node:fs' +import { + backfillFileFolderEntities, + backfillFileFolderEntitiesMigration, +} from '@sim/db/script-migrations/0031_backfill_file_folder_entities' +import { runScriptMigrations } from '@sim/db/script-migrations/index' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { createDeferred } from '@sim/testing/helpers/deferred' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import postgres, { type Sql } from 'postgres' +import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' + +const databaseUrl = readTestDatabaseUrl() +const migrations = ['0396_file_entity_ownership.sql', '0397_file_folder_version_ownership.sql'].map( + (name) => readFileSync(new URL(`./migrations/${name}`, import.meta.url), 'utf8') +) + +describe('file folder and version ownership in PostgreSQL', () => { + const schemaName = `file_relations_${generateId().replaceAll('-', '')}` + let sql: Sql + let admin: Sql + + async function applyMigration(migration: string) { + for (const statement of migration.split('--> statement-breakpoint')) { + if (statement.trim()) await sql.unsafe(statement) + } + } + + async function waitForDatabaseLock(pid: number) { + for (let attempt = 0; attempt < 100; attempt += 1) { + const [state] = await sql<{ waiting: boolean }[]>` + SELECT EXISTS ( + SELECT 1 FROM pg_stat_activity WHERE pid = ${pid} AND wait_event_type = 'Lock' + ) AS waiting + ` + if (state.waiting) return + await sleep(10) + } + throw new Error('Concurrent relationship operation did not wait for its database lock') + } + + beforeAll(async () => { + admin = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) + await admin.unsafe(`CREATE SCHEMA "${schemaName}"`) + sql = postgres(databaseUrl, { + max: 3, + onnotice: () => undefined, + connection: { search_path: schemaName, TimeZone: 'UTC' }, + }) + await sql`CREATE TABLE "user" (id text PRIMARY KEY)` + await sql`CREATE TABLE workspace (id text PRIMARY KEY)` + await sql`CREATE TABLE organization (id text PRIMARY KEY)` + await sql`CREATE TABLE project ( + id text PRIMARY KEY, name text NOT NULL DEFAULT 'Project', + owner_id text NOT NULL DEFAULT 'user-b' REFERENCES "user"(id) ON DELETE RESTRICT, + updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456' + )` + await sql`CREATE TABLE folder ( + id text PRIMARY KEY, name text NOT NULL, + user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, + workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE, + resource_type text NOT NULL DEFAULT 'workflow', + parent_id text REFERENCES folder(id) ON DELETE SET NULL, + deleted_at timestamp + )` + await sql`CREATE UNIQUE INDEX folder_workspace_resource_parent_name_active_unique + ON folder(workspace_id, resource_type, coalesce(parent_id, ''), name) + WHERE deleted_at IS NULL` + await sql`CREATE TABLE workspace_files ( + id text PRIMARY KEY, context text NOT NULL, + user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, + workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, + organization_id text REFERENCES organization(id) ON DELETE CASCADE, + folder_id text REFERENCES folder(id) ON DELETE SET NULL, + chat_id text, original_name text NOT NULL DEFAULT 'document.md', + key text NOT NULL DEFAULT 'unchanged-object-key', deleted_at timestamp, + content_updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456', + secret_provenance_version integer DEFAULT 1 + )` + await sql`CREATE UNIQUE INDEX workspace_files_workspace_folder_name_active_unique + ON workspace_files(workspace_id, coalesce(folder_id, ''), original_name) + WHERE deleted_at IS NULL AND context = 'workspace'` + await sql`CREATE TABLE workspace_file_version ( + id text PRIMARY KEY, file_id text NOT NULL REFERENCES workspace_files(id) ON DELETE CASCADE, + workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE, + version integer NOT NULL DEFAULT 1, key text NOT NULL DEFAULT 'unchanged-version-key', + UNIQUE(file_id, version) + )` + for (const migration of migrations) await applyMigration(migration) + }) + + beforeEach(async () => { + await sql`TRUNCATE workspace_file_version, workspace_files, folder, project, workspace, organization, "user"` + await sql`INSERT INTO "user" VALUES ('user-a'), ('user-b')` + await sql`INSERT INTO workspace VALUES ('workspace-a'), ('workspace-b'), ('same-id')` + await sql`INSERT INTO organization VALUES ('organization-a')` + await sql`INSERT INTO project (id) VALUES ('project-a'), ('project-b'), ('same-id')` + }) + + afterAll(async () => { + try { + await sql?.end() + await admin?.unsafe(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`) + } finally { + await admin?.end() + } + }) + + it('backfills several bounded pages without changing folder hierarchy or version attribution', async () => { + await sql`ALTER TABLE folder DISABLE TRIGGER folder_entity_binding` + await sql`ALTER TABLE workspace_file_version DISABLE TRIGGER workspace_file_version_owner_match` + try { + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + SELECT 'folder-' || lpad(id::text, 4, '0'), 'Folder ' || id, 'user-a', 'workspace-a', 'file' + FROM generate_series(1, 506) id` + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, parent_id) + VALUES ('child', 'Child', 'user-a', 'workspace-a', 'file', 'folder-0001')` + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a')` + await sql`INSERT INTO workspace_file_version (id, file_id, workspace_id) + VALUES ('bad-version', 'file', 'workspace-b')` + } finally { + await sql`ALTER TABLE folder ENABLE TRIGGER folder_entity_binding` + await sql`ALTER TABLE workspace_file_version ENABLE TRIGGER workspace_file_version_owner_match` + } + await sql`UPDATE folder SET name = 'Renamed' WHERE id = 'child'` + expect(await backfillFileFolderEntities(sql)).toEqual({ + backfilled: 507, + mismatchedVersions: 1, + sampleVersionIds: ['bad-version'], + }) + expect((await backfillFileFolderEntities(sql)).backfilled).toBe(0) + const [child] = + await sql`SELECT name, parent_id, entity_type, entity_id FROM folder WHERE id = 'child'` + expect(child).toEqual({ + name: 'Renamed', + parent_id: 'folder-0001', + entity_type: 'workspace', + entity_id: 'workspace-a', + }) + const [version] = + await sql`SELECT workspace_id, key FROM workspace_file_version WHERE id = 'bad-version'` + expect(version).toEqual({ workspace_id: 'workspace-b', key: 'unchanged-version-key' }) + }) + + it('preserves metadata and content changes on unbackfilled files with retained versions', async () => { + await sql`ALTER TABLE workspace_files DISABLE TRIGGER workspace_files_sync_entity_binding` + try { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a')` + } finally { + await sql`ALTER TABLE workspace_files ENABLE TRIGGER workspace_files_sync_entity_binding` + } + await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` + await sql`UPDATE workspace_files SET key = 'new-revision-key', original_name = 'renamed.md' WHERE id = 'file'` + await sql`UPDATE workspace_files SET entity_type = entity_type WHERE id = 'file'` + const [file] = + await sql`SELECT entity_type, entity_id, key, secret_provenance_version FROM workspace_files` + expect(file).toEqual({ + entity_type: 'workspace', + entity_id: 'workspace-a', + key: 'new-revision-key', + secret_provenance_version: 1, + }) + }) + + it('blocks release on conflicting retained ownership and retries after an explicit repair', async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a')` + await sql`ALTER TABLE workspace_file_version DISABLE TRIGGER workspace_file_version_owner_match` + try { + await sql`INSERT INTO workspace_file_version (id, file_id, workspace_id) + VALUES ('conflicting-version', 'file', 'workspace-b')` + } finally { + await sql`ALTER TABLE workspace_file_version ENABLE TRIGGER workspace_file_version_owner_match` + } + + await expect(runScriptMigrations(sql, [backfillFileFolderEntitiesMigration])).rejects.toThrow( + 'Retained file versions require ownership repair' + ) + expect(await sql`SELECT name FROM script_migrations`).toEqual([]) + expect( + await sql`SELECT workspace_id, key FROM workspace_file_version WHERE id = 'conflicting-version'` + ).toEqual([{ workspace_id: 'workspace-b', key: 'unchanged-version-key' }]) + + await sql`UPDATE workspace_file_version SET workspace_id = 'workspace-a' + WHERE id = 'conflicting-version'` + await runScriptMigrations(sql, [backfillFileFolderEntitiesMigration]) + await runScriptMigrations(sql, [backfillFileFolderEntitiesMigration]) + expect(await sql`SELECT name FROM script_migrations`).toEqual([ + { name: '0031_backfill_file_folder_entities' }, + ]) + expect( + await sql`SELECT workspace_id, key FROM workspace_file_version WHERE id = 'conflicting-version'` + ).toEqual([{ workspace_id: 'workspace-a', key: 'unchanged-version-key' }]) + }) + + it('requires explicit Project file-folder ownership and rejects unknown or conflicting bindings', async () => { + const [before] = await sql`SELECT * FROM project WHERE id = 'project-a'` + await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES ('project-folder', 'Documentation', 'user-a', 'file', 'project', 'project-a')` + const [after] = await sql`SELECT * FROM project WHERE id = 'project-a'` + expect(after).toEqual(before) + for (const resourceType of ['workflow', 'knowledge_base', 'table']) { + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES (${resourceType}, 'Invalid', 'user-a', ${resourceType}, 'project', 'project-a')`).rejects.toMatchObject( + { code: '23514' } + ) + } + for (const pair of [ + { type: null, id: null }, + { type: 'organization', id: 'organization-a' }, + { type: 'project', id: null }, + ]) { + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES ('invalid', 'Invalid', 'user-a', 'file', ${pair.type}, ${pair.id})`).rejects.toMatchObject( + { code: '23514' } + ) + } + await expect(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, entity_type, entity_id) + VALUES ('conflict', 'Invalid', 'user-a', 'workspace-a', 'file', 'project', 'project-a')`).rejects.toMatchObject( + { code: '23514' } + ) + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES ('missing', 'Invalid', 'user-a', 'file', 'project', 'missing')`).rejects.toMatchObject( + { code: '23503' } + ) + }) + + it('keeps folder identity and type immutable and rejects cross-owner parents', async () => { + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + VALUES ('file-folder', 'Files', 'user-a', 'workspace-a', 'file'), + ('other-folder', 'Files', 'user-a', 'workspace-b', 'file'), + ('workflow-folder', 'Workflows', 'user-a', 'workspace-a', 'workflow')` + await expect( + sql`UPDATE folder SET parent_id = 'other-folder' WHERE id = 'file-folder'` + ).rejects.toMatchObject({ code: '23514' }) + await expect( + sql`UPDATE folder SET parent_id = 'workflow-folder' WHERE id = 'file-folder'` + ).rejects.toMatchObject({ code: '23514' }) + await expect( + sql`UPDATE folder SET workspace_id = 'workspace-b', entity_id = 'workspace-b' WHERE id = 'file-folder'` + ).rejects.toMatchObject({ code: '23514' }) + await expect( + sql`UPDATE folder SET resource_type = 'table' WHERE id = 'file-folder'` + ).rejects.toMatchObject({ code: '23514' }) + await expect( + sql`UPDATE folder SET id = 'renamed-id' WHERE id = 'file-folder'` + ).rejects.toMatchObject({ code: '23514' }) + }) + + it('supports child-before-parent bulk creation and rejects cycles after the complete statement', async () => { + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, parent_id) + VALUES ('child', 'Child', 'user-a', 'workspace-a', 'file', 'parent'), + ('parent', 'Parent', 'user-a', 'workspace-a', 'file', NULL)` + await expect( + sql`UPDATE folder SET parent_id = 'child' WHERE id = 'parent'` + ).rejects.toMatchObject({ code: '23514' }) + await expect(sql`UPDATE folder SET parent_id = id WHERE id = 'parent'`).rejects.toMatchObject({ + code: '23514', + }) + await expect(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, parent_id) + VALUES ('cycle-a', 'A', 'user-a', 'workspace-a', 'file', 'cycle-b'), + ('cycle-b', 'B', 'user-a', 'workspace-a', 'file', 'cycle-a')`).rejects.toMatchObject({ + code: '23514', + }) + expect(await sql`SELECT id FROM folder ORDER BY id`).toEqual([ + { id: 'child' }, + { id: 'parent' }, + ]) + }) + + it('separates active names by owner type and parent and rejects conflicting restoration', async () => { + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + VALUES ('workspace-folder', 'Docs', 'user-a', 'same-id', 'file')` + await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES ('project-folder', 'Docs', 'user-a', 'file', 'project', 'same-id')` + await expect( + sql`UPDATE folder SET parent_id = 'workspace-folder' WHERE id = 'project-folder'` + ).rejects.toMatchObject({ code: '23514' }) + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES ('duplicate', 'Docs', 'user-a', 'file', 'project', 'same-id')`).rejects.toMatchObject({ + code: '23505', + }) + await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id, parent_id) + VALUES ('nested', 'Docs', 'user-a', 'file', 'project', 'same-id', 'project-folder')` + await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id, deleted_at) + VALUES ('deleted', 'Docs', 'user-a', 'file', 'project', 'same-id', now())` + await expect( + sql`UPDATE folder SET deleted_at = NULL WHERE id = 'deleted'` + ).rejects.toMatchObject({ code: '23505' }) + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('project-file', 'project', 'user-a', 'project', 'same-id')` + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('workspace-file', 'workspace', 'user-a', 'same-id')` + await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('duplicate-file', 'project', 'user-a', 'project', 'same-id')`).rejects.toMatchObject({ + code: '23505', + }) + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) + VALUES ('nested-file', 'project', 'user-a', 'project', 'same-id', 'project-folder')` + }) + + it('accepts Project file folders and rejects mismatched owners and resource types', async () => { + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + VALUES ('workspace-folder', 'Files', 'user-a', 'workspace-a', 'file'), + ('workflow-folder', 'Workflows', 'user-a', 'workspace-a', 'workflow')` + await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES ('project-folder', 'Docs', 'user-a', 'file', 'project', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) + VALUES ('project-file', 'project', 'user-a', 'project', 'project-a', 'project-folder')` + await expect( + sql`UPDATE workspace_files SET folder_id = 'workspace-folder' WHERE id = 'project-file'` + ).rejects.toMatchObject({ code: '23514' }) + await expect(sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, folder_id) + VALUES ('workspace-file', 'workspace', 'user-a', 'workspace-a', 'workflow-folder')`).rejects.toMatchObject( + { code: '23514' } + ) + await expect(sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, folder_id) + VALUES ('other-file', 'workspace', 'user-a', 'workspace-b', 'workspace-folder')`).rejects.toMatchObject( + { code: '23514' } + ) + }) + + it('protects retained Project folders from parent and creator deletion', async () => { + await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id, deleted_at) + VALUES ('project-folder', 'Docs', 'user-a', 'file', 'project', 'project-a', now())` + await expect(sql`DELETE FROM project WHERE id = 'project-a'`).rejects.toMatchObject({ + code: '23503', + }) + await expect(sql`DELETE FROM "user" WHERE id = 'user-a'`).rejects.toMatchObject({ + code: '23503', + }) + await sql`UPDATE folder SET user_id = 'user-b' WHERE id = 'project-folder'` + await sql`DELETE FROM "user" WHERE id = 'user-a'` + expect(await sql`SELECT id FROM folder`).toHaveLength(1) + await sql`DELETE FROM folder WHERE id = 'project-folder'` + await sql`DELETE FROM project WHERE id = 'project-a'` + }) + + it('derives version ownership from the file and refuses incompatible legacy pointers', async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('workspace-file', 'workspace', 'user-a', 'workspace-a'), + ('chat-file', 'mothership', 'user-a', 'workspace-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) + VALUES ('project-file', 'project', 'user-a', 'project', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id) VALUES ('personal', 'copilot', 'user-a')` + await sql`INSERT INTO workspace_file_version (id, file_id) + VALUES ('workspace-version', 'workspace-file'), ('project-version', 'project-file')` + expect(await sql`SELECT id, workspace_id FROM workspace_file_version ORDER BY id`).toEqual([ + { id: 'project-version', workspace_id: null }, + { id: 'workspace-version', workspace_id: 'workspace-a' }, + ]) + for (const fileId of ['project-file', 'chat-file', 'personal']) { + await expect(sql`INSERT INTO workspace_file_version (id, file_id, workspace_id, version) + VALUES ('invalid', ${fileId}, 'workspace-a', 2)`).rejects.toMatchObject({ code: '23514' }) + } + await expect(sql`INSERT INTO workspace_file_version (id, file_id, workspace_id, version) + VALUES ('mismatch', 'workspace-file', 'workspace-b', 2)`).rejects.toMatchObject({ + code: '23514', + }) + await expect( + sql`UPDATE workspace_file_version SET file_id = 'project-file' WHERE id = 'workspace-version'` + ).rejects.toMatchObject({ code: '23514' }) + await expect( + sql`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'workspace-file'` + ).rejects.toMatchObject({ code: '23514' }) + await expect( + sql`UPDATE workspace_files SET context = 'mothership' WHERE id = 'workspace-file'` + ).rejects.toMatchObject({ code: '23514' }) + await sql`DELETE FROM workspace_files WHERE id = 'project-file'` + expect( + await sql`SELECT id FROM workspace_file_version WHERE id = 'project-version'` + ).toHaveLength(0) + await sql`DELETE FROM workspace WHERE id = 'workspace-a'` + expect(await sql`SELECT id FROM workspace_file_version`).toHaveLength(0) + }) + + it.each([ + ['workspace', 'read committed'], + ['workspace', 'repeatable read'], + ['project', 'read committed'], + ['project', 'repeatable read'], + ])( + 'prevents concurrent crossed folder moves for %s at %s isolation', + async (ownerType, isolation) => { + const ownerId = ownerType === 'workspace' ? 'workspace-a' : 'project-a' + const workspaceId = ownerType === 'workspace' ? ownerId : null + const lockKey = `resource_folders:file:${ownerType === 'workspace' ? ownerId : `${ownerType}:${ownerId}`}` + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, entity_type, entity_id) + VALUES ('folder-a', 'A', 'user-a', ${workspaceId}, 'file', ${ownerType}, ${ownerId}), + ('folder-b', 'B', 'user-a', ${workspaceId}, 'file', ${ownerType}, ${ownerId})` + const moved = createDeferred() + const release = createDeferred() + const firstMove = sql.begin(async (tx) => { + await tx`SELECT pg_advisory_xact_lock(hashtextextended(${lockKey}, 0))` + await tx`UPDATE folder SET parent_id = 'folder-b' WHERE id = 'folder-a'` + moved.resolve() + await release.promise + }) + const waiting = createDeferred() + let rejection: Promise | undefined + try { + await moved.promise + const secondMove = sql.begin(`isolation level ${isolation}`, async (tx) => { + await tx`SET LOCAL statement_timeout = '5s'` + const [backend] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + waiting.resolve(backend.pid) + await tx`SELECT pg_advisory_xact_lock(hashtextextended(${lockKey}, 0))` + await tx`UPDATE folder SET parent_id = 'folder-a' WHERE id = 'folder-b'` + }) + rejection = expect(secondMove).rejects.toMatchObject({ + code: isolation === 'repeatable read' ? '40001' : '23514', + }) + await waitForDatabaseLock(await waiting.promise) + } finally { + release.resolve() + await firstMove + } + await rejection + expect(await sql`SELECT id, parent_id FROM folder ORDER BY id`).toEqual([ + { id: 'folder-a', parent_id: 'folder-b' }, + { id: 'folder-b', parent_id: null }, + ]) + } + ) + + it.each(['read committed', 'repeatable read'])( + 'prevents an owner change from racing a retained version at %s isolation', + async (isolation) => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a')` + const inserted = createDeferred() + const release = createDeferred() + const creation = sql.begin(async (tx) => { + await tx`INSERT INTO workspace_file_version (id, file_id, workspace_id) + VALUES ('version', 'file', 'workspace-a')` + inserted.resolve() + await release.promise + }) + const waiting = createDeferred() + let rejection: Promise | undefined + try { + await inserted.promise + const transfer = sql.begin(`isolation level ${isolation}`, async (tx) => { + await tx`SET LOCAL statement_timeout = '5s'` + const [backend] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + waiting.resolve(backend.pid) + await tx`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'file'` + }) + rejection = expect(transfer).rejects.toMatchObject({ + code: isolation === 'repeatable read' ? '40001' : '23514', + }) + await waitForDatabaseLock(await waiting.promise) + } finally { + release.resolve() + await creation + } + await rejection + const [file] = await sql`SELECT entity_id FROM workspace_files WHERE id = 'file'` + expect(file.entity_id).toBe('workspace-a') + } + ) + + it.each(['read committed', 'repeatable read'])( + 'rejects a stale version pointer after a concurrent owner change at %s isolation', + async (isolation) => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a')` + const moved = createDeferred() + const release = createDeferred() + const transfer = sql.begin(async (tx) => { + await tx`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'file'` + moved.resolve() + await release.promise + }) + const waiting = createDeferred() + let rejection: Promise | undefined + try { + await moved.promise + const creation = sql.begin(`isolation level ${isolation}`, async (tx) => { + await tx`SET LOCAL statement_timeout = '5s'` + const [backend] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + waiting.resolve(backend.pid) + await tx`INSERT INTO workspace_file_version (id, file_id, workspace_id) + VALUES ('version', 'file', 'workspace-a')` + }) + rejection = expect(creation).rejects.toMatchObject({ + code: isolation === 'repeatable read' ? '40001' : '23514', + }) + await waitForDatabaseLock(await waiting.promise) + } finally { + release.resolve() + await transfer + } + await rejection + expect(await sql`SELECT id FROM workspace_file_version`).toHaveLength(0) + } + ) + + it.each(['read committed', 'repeatable read'])( + 'protects a Project from concurrent folder creation and deletion at %s isolation', + async (isolation) => { + const inserted = createDeferred() + const release = createDeferred() + const creation = sql.begin(async (tx) => { + await tx`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES ('folder', 'Docs', 'user-a', 'file', 'project', 'project-a')` + inserted.resolve() + await release.promise + }) + const waiting = createDeferred() + let rejection: Promise | undefined + try { + await inserted.promise + const deletion = sql.begin(`isolation level ${isolation}`, async (tx) => { + await tx`SET LOCAL statement_timeout = '5s'` + const [backend] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + waiting.resolve(backend.pid) + await tx`DELETE FROM project WHERE id = 'project-a'` + }) + rejection = expect(deletion).rejects.toMatchObject({ + code: isolation === 'repeatable read' ? '40001' : '23503', + }) + await waitForDatabaseLock(await waiting.promise) + } finally { + release.resolve() + await creation + } + await rejection + expect(await sql`SELECT id FROM folder WHERE id = 'folder'`).toHaveLength(1) + } + ) + + it('replays the expansion while preserving existing Project folders and version pointers', async () => { + await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES ('folder', 'Docs', 'user-a', 'file', 'project', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) + VALUES ('file', 'project', 'user-a', 'project', 'project-a', 'folder')` + await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` + await applyMigration(migrations[1]) + const [file] = await sql`SELECT folder_id, key, + content_updated_at = TIMESTAMP '2026-10-03 01:02:03.456' AS revision_unchanged, + secret_provenance_version FROM workspace_files` + expect(file).toMatchObject({ + folder_id: 'folder', + key: 'unchanged-object-key', + secret_provenance_version: 1, + revision_unchanged: true, + }) + expect(await sql`SELECT file_id, key FROM workspace_file_version`).toEqual([ + { file_id: 'file', key: 'unchanged-version-key' }, + ]) + }) +}) + +describe('file ownership composed with enforced Project membership', () => { + it('preserves Project lifecycle checks and public metadata through folder, file, and version writes', async () => { + const admin = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) + const name = `file_owner_chain_test_${generateId().replaceAll('-', '')}` + const url = new URL(databaseUrl) + url.pathname = `/${name}` + await admin.unsafe(`CREATE DATABASE "${name}"`) + const chain = postgres(url.toString(), { max: 1, onnotice: () => undefined }) + try { + await chain.unsafe(` + CREATE TABLE "user" (id text PRIMARY KEY); + CREATE TABLE organization (id text PRIMARY KEY); + CREATE TABLE workspace ( + id text PRIMARY KEY, name text NOT NULL, owner_id text NOT NULL, + organization_id text, archived_at timestamp, + forked_from_workspace_id text REFERENCES workspace(id) ON DELETE SET NULL + ); + CREATE TABLE workflow ( + id text PRIMARY KEY, workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, + archived_at timestamp + ); + CREATE TABLE folder ( + id text PRIMARY KEY, name text NOT NULL, + user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, + workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE, + resource_type text NOT NULL, parent_id text REFERENCES folder(id) ON DELETE SET NULL, + deleted_at timestamp + ); + CREATE TABLE workspace_files ( + id text PRIMARY KEY, context text NOT NULL, + user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, + workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, + organization_id text REFERENCES organization(id) ON DELETE CASCADE, + folder_id text REFERENCES folder(id) ON DELETE SET NULL, + chat_id text, original_name text NOT NULL DEFAULT 'document.md', deleted_at timestamp + ); + CREATE TABLE workspace_file_version ( + id text PRIMARY KEY, file_id text NOT NULL REFERENCES workspace_files(id) ON DELETE CASCADE, + workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE + ); + INSERT INTO "user" VALUES ('owner'), ('creator'); + INSERT INTO workspace (id, name, owner_id) VALUES ('environment', 'Production', 'owner'); + `) + for (const source of [ + readFileSync(new URL('./migrations/0394_project_foundation.sql', import.meta.url), 'utf8'), + readFileSync( + new URL('./migrations/0395_project_membership_enforcement.sql', import.meta.url), + 'utf8' + ), + ...migrations, + ]) { + for (const statement of source.split('--> statement-breakpoint')) { + if (statement.trim()) await chain.unsafe(statement) + } + } + const [project] = await chain<{ id: string }[]>`SELECT id FROM project` + const [before] = await chain`SELECT * FROM project WHERE id = ${project.id}` + await chain`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) + VALUES ('folder', 'Architecture', 'creator', 'file', 'project', ${project.id})` + await chain`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) + VALUES ('file', 'project', 'creator', 'project', ${project.id}, 'folder')` + await chain`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` + const [after] = await chain`SELECT * FROM project WHERE id = ${project.id}` + expect(after).toEqual(before) + await expect(chain`DELETE FROM "user" WHERE id = 'creator'`).rejects.toMatchObject({ + code: '23503', + }) + await expect(chain`DELETE FROM workspace WHERE id = 'environment'`).rejects.toMatchObject({ + code: '23514', + }) + await chain.begin(async (tx) => { + await tx`UPDATE workspace SET archived_at = now() WHERE id = 'environment'` + await tx`UPDATE project SET archived_at = now() WHERE id = ${project.id}` + }) + expect(await chain`SELECT id, workspace_id FROM workspace_file_version`).toEqual([ + { id: 'version', workspace_id: null }, + ]) + expect(await chain`SELECT id FROM folder`).toEqual([{ id: 'folder' }]) + expect(await chain`SELECT id FROM workspace_files`).toEqual([{ id: 'file' }]) + } finally { + await chain.end({ timeout: 2 }) + await admin.unsafe(`DROP DATABASE "${name}" WITH (FORCE)`) + await admin.end() + } + }) +}) diff --git a/packages/db/migrations/0396_file_entity_ownership.sql b/packages/db/migrations/0396_file_entity_ownership.sql new file mode 100644 index 00000000000..303cc94e6e0 --- /dev/null +++ b/packages/db/migrations/0396_file_entity_ownership.sql @@ -0,0 +1,144 @@ +-- Additive ownership metadata. Legacy columns/FKs remain authoritative for legacy writers. +ALTER TABLE "workspace_files" ADD COLUMN IF NOT EXISTS "entity_type" text;--> statement-breakpoint +ALTER TABLE "workspace_files" ADD COLUMN IF NOT EXISTS "entity_id" text;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint + WHERE conname = 'workspace_files_entity_binding_check' + AND conrelid = 'workspace_files'::regclass + ) THEN + ALTER TABLE "workspace_files" ADD CONSTRAINT "workspace_files_entity_binding_check" + CHECK ( + (entity_type IS NULL AND entity_id IS NULL) + OR (entity_type IS NOT NULL AND entity_id IS NOT NULL + AND entity_type IN ('workspace', 'project', 'organization', 'user') + AND char_length(entity_id) > 0) + ) NOT VALID; + END IF; +END $$;--> statement-breakpoint +-- This mapping is deliberately incomplete. Unknown legacy contexts stay unbound until audited. +CREATE OR REPLACE FUNCTION workspace_file_legacy_entity( + file_context text, file_workspace_id text, file_organization_id text, file_user_id text +) RETURNS TABLE (entity_type text, entity_id text) +LANGUAGE sql IMMUTABLE AS $$ + SELECT CASE + WHEN file_organization_id IS NULL AND file_workspace_id IS NOT NULL + AND file_context IN ('workspace', 'mothership', 'execution', 'workspace-logos', 'knowledge-base') + THEN 'workspace' + WHEN file_workspace_id IS NULL AND file_organization_id IS NOT NULL + AND file_context = 'knowledge-base' THEN 'organization' + WHEN file_workspace_id IS NULL AND file_organization_id IS NULL + AND file_context IN ('copilot', 'profile-pictures') THEN 'user' + END, + CASE + WHEN file_organization_id IS NULL AND file_workspace_id IS NOT NULL + AND file_context IN ('workspace', 'mothership', 'execution', 'workspace-logos', 'knowledge-base') + THEN file_workspace_id + WHEN file_workspace_id IS NULL AND file_organization_id IS NOT NULL + AND file_context = 'knowledge-base' THEN file_organization_id + WHEN file_workspace_id IS NULL AND file_organization_id IS NULL + AND file_context IN ('copilot', 'profile-pictures') THEN file_user_id + END +$$;--> statement-breakpoint +CREATE OR REPLACE FUNCTION workspace_files_sync_entity_binding() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE + expected_type text; + expected_id text; + legacy_changed boolean := false; + pair_changed boolean := false; +BEGIN + IF TG_OP = 'UPDATE' THEN + legacy_changed := ROW(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id) + IS DISTINCT FROM ROW(OLD.context, OLD.workspace_id, OLD.organization_id, OLD.user_id); + pair_changed := ROW(NEW.entity_type, NEW.entity_id) + IS DISTINCT FROM ROW(OLD.entity_type, OLD.entity_id); + IF OLD.entity_type = 'project' AND pair_changed THEN + RAISE EXCEPTION 'Project file ownership cannot change through a metadata update' + USING ERRCODE = '23514'; + END IF; + END IF; + + IF NEW.entity_type = 'project' THEN + IF NEW.entity_id IS NULL OR NEW.entity_id = '' OR NEW.context <> 'project' + OR NEW.workspace_id IS NOT NULL OR NEW.organization_id IS NOT NULL + OR NEW.chat_id IS NOT NULL OR NEW.folder_id IS NOT NULL THEN + RAISE EXCEPTION 'Project file binding conflicts with legacy ownership or an unsupported folder' + USING ERRCODE = '23514'; + END IF; + IF TG_OP = 'INSERT' OR OLD.entity_type IS DISTINCT FROM 'project' THEN + -- A new row version fences parent deletion from an older REPEATABLE READ snapshot. + -- A read-only parent lock cannot make that snapshot see this newly inserted child. + UPDATE project SET id = id WHERE id = NEW.entity_id; + ELSE + PERFORM 1 FROM project WHERE id = NEW.entity_id FOR KEY SHARE; + END IF; + IF NOT FOUND THEN + RAISE EXCEPTION 'Project file owner does not exist' USING ERRCODE = '23503'; + END IF; + RETURN NEW; + END IF; + + SELECT entity_type, entity_id INTO expected_type, expected_id + FROM workspace_file_legacy_entity(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id); + + IF (NEW.entity_type IS NULL AND NEW.entity_id IS NULL) + OR (TG_OP = 'UPDATE' AND legacy_changed AND NOT pair_changed) THEN + NEW.entity_type := expected_type; + NEW.entity_id := expected_id; + ELSIF ROW(NEW.entity_type, NEW.entity_id) IS DISTINCT FROM ROW(expected_type, expected_id) THEN + RAISE EXCEPTION 'File entity binding conflicts with its legacy owner or context' + USING ERRCODE = '23514'; + END IF; + + IF NEW.context = 'project' THEN + RAISE EXCEPTION 'Project files require explicit Project ownership' USING ERRCODE = '23514'; + END IF; + RETURN NEW; +END; +$$;--> statement-breakpoint +DROP TRIGGER IF EXISTS workspace_files_sync_entity_binding ON workspace_files;--> statement-breakpoint +CREATE TRIGGER workspace_files_sync_entity_binding +BEFORE INSERT OR UPDATE OF entity_type, entity_id, workspace_id, organization_id, user_id, context, folder_id, chat_id +ON workspace_files FOR EACH ROW EXECUTE FUNCTION workspace_files_sync_entity_binding();--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_file_owner_delete_guard() +RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + IF TG_OP = 'UPDATE' AND NEW.id = OLD.id THEN RETURN NEW; END IF; + IF EXISTS ( + SELECT 1 FROM workspace_files WHERE entity_type = 'project' AND entity_id = OLD.id + ) THEN + RAISE EXCEPTION 'Project files must be retired before their owner is removed' + USING ERRCODE = '23503'; + END IF; + IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; + RETURN NEW; +END; +$$;--> statement-breakpoint +DROP TRIGGER IF EXISTS project_file_owner_delete_guard ON project;--> statement-breakpoint +CREATE TRIGGER project_file_owner_delete_guard +BEFORE DELETE OR UPDATE OF id ON project +FOR EACH ROW EXECUTE FUNCTION project_file_owner_delete_guard();--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() +RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + IF EXISTS ( + SELECT 1 FROM workspace_files WHERE entity_type = 'project' AND user_id = OLD.id + ) THEN + RAISE EXCEPTION 'Project file attribution must be reassigned or retired before deleting its uploader' + USING ERRCODE = '23503'; + END IF; + RETURN OLD; +END; +$$;--> statement-breakpoint +DROP TRIGGER IF EXISTS project_file_uploader_delete_guard ON "user";--> statement-breakpoint +CREATE TRIGGER project_file_uploader_delete_guard +BEFORE DELETE ON "user" +FOR EACH ROW EXECUTE FUNCTION project_file_uploader_delete_guard();--> statement-breakpoint +-- A failed concurrent build is replayable; do not retain an invalid same-name index. +COMMIT;--> statement-breakpoint +SET lock_timeout = 0;--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "workspace_files_entity_id_idx";--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_entity_id_idx" +ON "workspace_files" USING btree ("entity_type", "entity_id", "id");--> statement-breakpoint +SET lock_timeout = '5s'; diff --git a/packages/db/migrations/0397_file_folder_version_ownership.sql b/packages/db/migrations/0397_file_folder_version_ownership.sql new file mode 100644 index 00000000000..e16243a33f0 --- /dev/null +++ b/packages/db/migrations/0397_file_folder_version_ownership.sql @@ -0,0 +1,297 @@ +-- Nullable compatibility fields preserve all legacy writers while adding Project file folders. +ALTER TABLE "folder" ADD COLUMN IF NOT EXISTS "entity_type" text;--> statement-breakpoint +ALTER TABLE "folder" ADD COLUMN IF NOT EXISTS "entity_id" text;--> statement-breakpoint +ALTER TABLE "folder" ALTER COLUMN "workspace_id" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "workspace_file_version" ALTER COLUMN "workspace_id" DROP NOT NULL;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint WHERE conname = 'folder_entity_binding_check' + AND conrelid = 'folder'::regclass + ) THEN + ALTER TABLE folder ADD CONSTRAINT folder_entity_binding_check CHECK ( + (entity_type IS NULL AND entity_id IS NULL AND workspace_id IS NOT NULL) + OR (entity_type IS NOT NULL AND entity_id IS NOT NULL AND char_length(entity_id) > 0 + AND ((entity_type = 'workspace' AND workspace_id IS NOT NULL AND entity_id = workspace_id) + OR (entity_type = 'project' AND workspace_id IS NULL AND resource_type = 'file'))) + ) NOT VALID; + END IF; +END $$;--> statement-breakpoint +CREATE OR REPLACE FUNCTION folder_sync_entity_binding() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE + previous_type text; + previous_id text; + hierarchy_changed boolean := true; + lock_key text; +BEGIN + IF NEW.workspace_id IS NOT NULL THEN + IF NEW.entity_type IS NULL AND NEW.entity_id IS NULL THEN + NEW.entity_type := 'workspace'; + NEW.entity_id := NEW.workspace_id; + ELSIF NEW.entity_type IS DISTINCT FROM 'workspace' OR NEW.entity_id IS DISTINCT FROM NEW.workspace_id THEN + RAISE EXCEPTION 'Folder entity conflicts with its workspace' USING ERRCODE = '23514'; + END IF; + ELSIF NEW.entity_type IS DISTINCT FROM 'project' OR NEW.entity_id IS NULL OR NEW.entity_id = '' + OR NEW.resource_type <> 'file' THEN + RAISE EXCEPTION 'Only Project file folders may omit workspace ownership' USING ERRCODE = '23514'; + END IF; + + IF TG_OP = 'UPDATE' THEN + previous_type := coalesce(OLD.entity_type, 'workspace'); + previous_id := coalesce(OLD.entity_id, OLD.workspace_id); + IF ROW(previous_type, previous_id, OLD.resource_type, OLD.id) + IS DISTINCT FROM ROW(NEW.entity_type, NEW.entity_id, NEW.resource_type, NEW.id) THEN + RAISE EXCEPTION 'Folder identity, owner, and resource type cannot be changed' USING ERRCODE = '23514'; + END IF; + hierarchy_changed := NEW.parent_id IS DISTINCT FROM OLD.parent_id; + END IF; + + IF NEW.entity_type = 'project' THEN + IF TG_OP = 'INSERT' THEN + -- Fence stale-snapshot Project deletion without changing public parent fields. + UPDATE project SET id = id WHERE id = NEW.entity_id; + ELSE + PERFORM 1 FROM project WHERE id = NEW.entity_id FOR KEY SHARE; + END IF; + IF NOT FOUND THEN RAISE EXCEPTION 'Project folder owner does not exist' USING ERRCODE = '23503'; END IF; + END IF; + + IF hierarchy_changed THEN + -- Existing workspace writers already use this key before taking folder row locks. + lock_key := 'resource_folders:' || NEW.resource_type::text || ':' || + CASE WHEN NEW.entity_type = 'workspace' THEN NEW.entity_id ELSE NEW.entity_type || ':' || NEW.entity_id END; + PERFORM pg_advisory_xact_lock(hashtextextended(lock_key, 0)); + END IF; + RETURN NEW; +END; +$$;--> statement-breakpoint +DROP TRIGGER IF EXISTS folder_entity_binding ON folder;--> statement-breakpoint +CREATE TRIGGER folder_entity_binding +BEFORE INSERT OR UPDATE OF id, entity_type, entity_id, workspace_id, resource_type, parent_id +ON folder FOR EACH ROW EXECUTE FUNCTION folder_sync_entity_binding();--> statement-breakpoint +CREATE OR REPLACE FUNCTION folder_parent_resource_type_match() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE + child folder%ROWTYPE; + ancestor folder%ROWTYPE; + ancestor_id text; + seen text[]; +BEGIN + SELECT * INTO child FROM folder WHERE id = NEW.id; + IF NOT FOUND THEN RETURN NEW; END IF; + child.entity_type := coalesce(child.entity_type, 'workspace'); + child.entity_id := coalesce(child.entity_id, child.workspace_id); + IF TG_OP = 'UPDATE' AND OLD.parent_id IS NOT DISTINCT FROM child.parent_id + AND OLD.resource_type = child.resource_type + AND ROW(coalesce(OLD.entity_type, 'workspace'), coalesce(OLD.entity_id, OLD.workspace_id)) + IS NOT DISTINCT FROM ROW(child.entity_type, child.entity_id) THEN + RETURN NEW; + END IF; + ancestor_id := child.parent_id; + seen := ARRAY[child.id]; + WHILE ancestor_id IS NOT NULL LOOP + IF ancestor_id = ANY(seen) THEN + RAISE EXCEPTION 'Folder hierarchy contains a cycle' USING ERRCODE = '23514'; + END IF; + seen := array_append(seen, ancestor_id); + -- Lock every ancestor, so a stale REPEATABLE READ traversal cannot miss a concurrent move. + SELECT * INTO ancestor FROM folder WHERE id = ancestor_id FOR SHARE; + IF NOT FOUND THEN RAISE EXCEPTION 'Folder parent does not exist' USING ERRCODE = '23503'; END IF; + IF ancestor.resource_type <> child.resource_type + OR ROW(coalesce(ancestor.entity_type, 'workspace'), coalesce(ancestor.entity_id, ancestor.workspace_id)) + IS DISTINCT FROM ROW(child.entity_type, child.entity_id) THEN + RAISE EXCEPTION 'Folder parent belongs to a different owner or resource type' USING ERRCODE = '23514'; + END IF; + ancestor_id := ancestor.parent_id; + END LOOP; + RETURN NEW; +END; +$$;--> statement-breakpoint +DROP TRIGGER IF EXISTS folder_parent_resource_type_match ON folder;--> statement-breakpoint +-- End-of-statement checks preserve the supported child-before-parent bulk insert order. +CREATE CONSTRAINT TRIGGER folder_parent_resource_type_match +AFTER INSERT OR UPDATE ON folder DEFERRABLE INITIALLY IMMEDIATE +FOR EACH ROW EXECUTE FUNCTION folder_parent_resource_type_match();--> statement-breakpoint +CREATE OR REPLACE FUNCTION workspace_files_sync_entity_binding() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE + expected_type text; + expected_id text; + legacy_changed boolean := false; + pair_changed boolean := false; +BEGIN + IF TG_OP = 'UPDATE' THEN + legacy_changed := ROW(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id) + IS DISTINCT FROM ROW(OLD.context, OLD.workspace_id, OLD.organization_id, OLD.user_id); + pair_changed := ROW(NEW.entity_type, NEW.entity_id) + IS DISTINCT FROM ROW(OLD.entity_type, OLD.entity_id); + IF OLD.entity_type = 'project' AND pair_changed THEN + RAISE EXCEPTION 'Project file ownership cannot change through a metadata update' + USING ERRCODE = '23514'; + END IF; + END IF; + + IF NEW.entity_type = 'project' THEN + IF NEW.entity_id IS NULL OR NEW.entity_id = '' OR NEW.context <> 'project' + OR NEW.workspace_id IS NOT NULL OR NEW.organization_id IS NOT NULL + OR NEW.chat_id IS NOT NULL THEN + RAISE EXCEPTION 'Project file binding conflicts with legacy ownership' + USING ERRCODE = '23514'; + END IF; + IF TG_OP = 'INSERT' OR OLD.entity_type IS DISTINCT FROM 'project' THEN + -- A new row version fences parent deletion from an older REPEATABLE READ snapshot. + -- A read-only parent lock cannot make that snapshot see this newly inserted child. + UPDATE project SET id = id WHERE id = NEW.entity_id; + ELSE + PERFORM 1 FROM project WHERE id = NEW.entity_id FOR KEY SHARE; + END IF; + IF NOT FOUND THEN + RAISE EXCEPTION 'Project file owner does not exist' USING ERRCODE = '23503'; + END IF; + RETURN NEW; + END IF; + + SELECT entity_type, entity_id INTO expected_type, expected_id + FROM workspace_file_legacy_entity(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id); + + IF (NEW.entity_type IS NULL AND NEW.entity_id IS NULL) + OR (TG_OP = 'UPDATE' AND legacy_changed AND NOT pair_changed) THEN + NEW.entity_type := expected_type; + NEW.entity_id := expected_id; + ELSIF ROW(NEW.entity_type, NEW.entity_id) IS DISTINCT FROM ROW(expected_type, expected_id) THEN + RAISE EXCEPTION 'File entity binding conflicts with its legacy owner or context' + USING ERRCODE = '23514'; + END IF; + + IF NEW.context = 'project' THEN + RAISE EXCEPTION 'Project files require explicit Project ownership' USING ERRCODE = '23514'; + END IF; + RETURN NEW; +END; +$$;--> statement-breakpoint +CREATE OR REPLACE FUNCTION workspace_files_validate_relations() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE + previous_type text; + previous_id text; + current_type text; + current_id text; + target folder%ROWTYPE; +BEGIN + current_type := NEW.entity_type; + current_id := NEW.entity_id; + IF current_type IS NULL AND current_id IS NULL THEN + SELECT entity_type, entity_id INTO current_type, current_id + FROM workspace_file_legacy_entity(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id); + END IF; + IF TG_OP = 'UPDATE' THEN + previous_type := OLD.entity_type; + previous_id := OLD.entity_id; + IF previous_type IS NULL AND previous_id IS NULL THEN + SELECT entity_type, entity_id INTO previous_type, previous_id + FROM workspace_file_legacy_entity(OLD.context, OLD.workspace_id, OLD.organization_id, OLD.user_id); + END IF; + IF (ROW(previous_type, previous_id) IS DISTINCT FROM ROW(current_type, current_id) + OR NEW.context NOT IN ('workspace', 'project')) + AND EXISTS (SELECT 1 FROM workspace_file_version WHERE file_id = NEW.id) THEN + RAISE EXCEPTION 'A file with retained history cannot change its owner' USING ERRCODE = '23514'; + END IF; + IF OLD.folder_id IS NOT DISTINCT FROM NEW.folder_id + AND ROW(previous_type, previous_id) IS NOT DISTINCT FROM ROW(current_type, current_id) THEN + RETURN NEW; + END IF; + END IF; + IF NEW.folder_id IS NOT NULL THEN + SELECT * INTO target FROM folder WHERE id = NEW.folder_id FOR SHARE; + IF NOT FOUND THEN RAISE EXCEPTION 'File folder does not exist' USING ERRCODE = '23503'; END IF; + IF target.resource_type <> 'file' OR current_type IS NULL OR current_id IS NULL + OR ROW(coalesce(target.entity_type, 'workspace'), coalesce(target.entity_id, target.workspace_id)) + IS DISTINCT FROM ROW(current_type, current_id) THEN + RAISE EXCEPTION 'File folder belongs to a different owner or resource type' USING ERRCODE = '23514'; + END IF; + END IF; + RETURN NEW; +END; +$$;--> statement-breakpoint +DROP TRIGGER IF EXISTS workspace_files_validate_relations ON workspace_files;--> statement-breakpoint +CREATE CONSTRAINT TRIGGER workspace_files_validate_relations +AFTER INSERT OR UPDATE ON workspace_files DEFERRABLE INITIALLY IMMEDIATE +FOR EACH ROW EXECUTE FUNCTION workspace_files_validate_relations();--> statement-breakpoint +CREATE OR REPLACE FUNCTION workspace_file_version_owner_match() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE + parent workspace_files%ROWTYPE; + parent_type text; + parent_id text; +BEGIN + IF TG_OP = 'UPDATE' AND NEW.file_id IS DISTINCT FROM OLD.file_id THEN + RAISE EXCEPTION 'A retained version cannot be moved to another file' USING ERRCODE = '23514'; + END IF; + IF TG_OP = 'INSERT' THEN + -- Fence owner changes against versions committed after an older transaction snapshot. + UPDATE workspace_files SET id = id WHERE id = NEW.file_id RETURNING * INTO parent; + ELSE + SELECT * INTO parent FROM workspace_files WHERE id = NEW.file_id FOR SHARE; + END IF; + IF NOT FOUND THEN RAISE EXCEPTION 'Version file does not exist' USING ERRCODE = '23503'; END IF; + parent_type := parent.entity_type; + parent_id := parent.entity_id; + IF parent_type IS NULL AND parent_id IS NULL THEN + SELECT entity_type, entity_id INTO parent_type, parent_id + FROM workspace_file_legacy_entity(parent.context, parent.workspace_id, parent.organization_id, parent.user_id); + END IF; + IF parent_type = 'workspace' AND parent.context = 'workspace' THEN + IF NEW.workspace_id IS NULL THEN NEW.workspace_id := parent_id; END IF; + IF NEW.workspace_id IS DISTINCT FROM parent_id THEN + RAISE EXCEPTION 'Version workspace differs from its file owner' USING ERRCODE = '23514'; + END IF; + ELSIF parent_type = 'project' AND parent.context = 'project' AND NEW.workspace_id IS NULL THEN + NULL; + ELSE + RAISE EXCEPTION 'File version has an unsupported or conflicting owner' USING ERRCODE = '23514'; + END IF; + RETURN NEW; +END; +$$;--> statement-breakpoint +DROP TRIGGER IF EXISTS workspace_file_version_owner_match ON workspace_file_version;--> statement-breakpoint +CREATE TRIGGER workspace_file_version_owner_match +BEFORE INSERT OR UPDATE OF file_id, workspace_id ON workspace_file_version +FOR EACH ROW EXECUTE FUNCTION workspace_file_version_owner_match();--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_file_owner_delete_guard() +RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + IF TG_OP = 'UPDATE' AND NEW.id = OLD.id THEN RETURN NEW; END IF; + IF EXISTS (SELECT 1 FROM workspace_files WHERE entity_type = 'project' AND entity_id = OLD.id) + OR EXISTS (SELECT 1 FROM folder WHERE entity_type = 'project' AND entity_id = OLD.id) THEN + RAISE EXCEPTION 'Project files and folders must be retired before their owner is removed' + USING ERRCODE = '23503'; + END IF; + IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; + RETURN NEW; +END; +$$;--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() +RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + IF EXISTS (SELECT 1 FROM workspace_files WHERE entity_type = 'project' AND user_id = OLD.id) + OR EXISTS (SELECT 1 FROM folder WHERE entity_type = 'project' AND user_id = OLD.id) THEN + RAISE EXCEPTION 'Project file and folder attribution must be reassigned or retired before deleting its creator' + USING ERRCODE = '23503'; + END IF; + RETURN OLD; +END; +$$;--> statement-breakpoint +COMMIT;--> statement-breakpoint +SET lock_timeout = 0;--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "folder_entity_id_idx";--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "folder_entity_id_idx" +ON "folder" USING btree ("entity_type", "entity_id", "id");--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "folder_entity_resource_parent_name_active_unique";--> statement-breakpoint +CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "folder_entity_resource_parent_name_active_unique" +ON "folder" USING btree ("entity_type", "entity_id", "resource_type", coalesce("parent_id", ''), "name") +WHERE "deleted_at" IS NULL;--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "workspace_files_entity_folder_name_active_unique";--> statement-breakpoint +CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_entity_folder_name_active_unique" +ON "workspace_files" USING btree ("entity_type", "entity_id", coalesce("folder_id", ''), "original_name") +WHERE "deleted_at" IS NULL AND "context" IN ('workspace', 'project');--> statement-breakpoint +SET lock_timeout = '5s'; diff --git a/packages/db/migrations/0398_file_creator_lifetime.sql b/packages/db/migrations/0398_file_creator_lifetime.sql new file mode 100644 index 00000000000..6b00ae8e242 --- /dev/null +++ b/packages/db/migrations/0398_file_creator_lifetime.sql @@ -0,0 +1,75 @@ +ALTER TABLE "folder" ADD COLUMN IF NOT EXISTS "original_creator_user_id" text;--> statement-breakpoint +ALTER TABLE "workspace_files" ADD COLUMN IF NOT EXISTS "original_creator_user_id" text;--> statement-breakpoint +ALTER TABLE "folder" ALTER COLUMN "user_id" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "workspace_files" ALTER COLUMN "user_id" DROP NOT NULL;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_creator_lifetime_check' AND conrelid = 'folder'::regclass) THEN + ALTER TABLE folder ADD CONSTRAINT folder_creator_lifetime_check CHECK ( + user_id IS NOT NULL OR coalesce(entity_type = 'project' AND char_length(original_creator_user_id) > 0, false) + ) NOT VALID; + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'workspace_files_creator_lifetime_check' AND conrelid = 'workspace_files'::regclass) THEN + ALTER TABLE workspace_files ADD CONSTRAINT workspace_files_creator_lifetime_check CHECK ( + user_id IS NOT NULL OR coalesce(entity_type = 'project' AND char_length(original_creator_user_id) > 0, false) + ) NOT VALID; + END IF; +END $$;--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_file_creator_snapshot() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE + original_creator text; +BEGIN + IF TG_OP = 'INSERT' THEN + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'New file resources require their actual creator' USING ERRCODE = '23514'; + END IF; + IF NEW.entity_type = 'project' THEN + IF NEW.original_creator_user_id IS NOT NULL AND NEW.original_creator_user_id IS DISTINCT FROM NEW.user_id THEN + RAISE EXCEPTION 'Project creator snapshot must match its actual creator' USING ERRCODE = '23514'; + END IF; + NEW.original_creator_user_id := NEW.user_id; + ELSIF NEW.original_creator_user_id IS NOT NULL THEN + RAISE EXCEPTION 'Creator snapshots are reserved for Project resources' USING ERRCODE = '23514'; + END IF; + ELSE + original_creator := coalesce(OLD.original_creator_user_id, + CASE WHEN OLD.entity_type = 'project' OR NEW.entity_type = 'project' THEN OLD.user_id ELSE NULL END); + IF original_creator IS NOT NULL THEN + IF (OLD.original_creator_user_id IS NOT NULL AND NEW.original_creator_user_id IS DISTINCT FROM OLD.original_creator_user_id) + OR (NEW.original_creator_user_id IS NOT NULL AND NEW.original_creator_user_id IS DISTINCT FROM original_creator) + OR (NEW.user_id IS NOT NULL AND NEW.user_id IS DISTINCT FROM original_creator) THEN + RAISE EXCEPTION 'Project original creator attribution cannot be changed' USING ERRCODE = '23514'; + END IF; + NEW.original_creator_user_id := original_creator; + ELSIF NEW.original_creator_user_id IS NOT NULL THEN + RAISE EXCEPTION 'Project original creator attribution is unavailable' USING ERRCODE = '23514'; + END IF; + END IF; + IF NEW.user_id IS NULL AND (NEW.entity_type IS DISTINCT FROM 'project' + OR NEW.original_creator_user_id IS NULL OR NEW.original_creator_user_id = '') THEN + RAISE EXCEPTION 'Only attributed Project resources may outlive their creator account' USING ERRCODE = '23514'; + END IF; + RETURN NEW; +END; +$$;--> statement-breakpoint +DROP TRIGGER IF EXISTS folder_creator_lifetime ON folder;--> statement-breakpoint +CREATE TRIGGER folder_creator_lifetime +BEFORE INSERT OR UPDATE OF user_id, original_creator_user_id, entity_type ON folder +FOR EACH ROW EXECUTE FUNCTION project_file_creator_snapshot();--> statement-breakpoint +DROP TRIGGER IF EXISTS workspace_files_creator_lifetime ON workspace_files;--> statement-breakpoint +CREATE TRIGGER workspace_files_creator_lifetime +BEFORE INSERT OR UPDATE OF user_id, original_creator_user_id, entity_type ON workspace_files +FOR EACH ROW EXECUTE FUNCTION project_file_creator_snapshot();--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() +RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + -- Retain original authorship without substituting a new owner, payer, or creator. + UPDATE workspace_files + SET original_creator_user_id = coalesce(original_creator_user_id, user_id), user_id = NULL + WHERE entity_type = 'project' AND user_id = OLD.id; + UPDATE folder + SET original_creator_user_id = coalesce(original_creator_user_id, user_id), user_id = NULL + WHERE entity_type = 'project' AND user_id = OLD.id; + RETURN OLD; +END; +$$;--> statement-breakpoint diff --git a/packages/db/migrations/0399_project_file_upload_sessions.sql b/packages/db/migrations/0399_project_file_upload_sessions.sql new file mode 100644 index 00000000000..314d361e6fd --- /dev/null +++ b/packages/db/migrations/0399_project_file_upload_sessions.sql @@ -0,0 +1 @@ +ALTER TYPE "public"."upload_session_purpose" ADD VALUE 'project_file' BEFORE 'table_import'; \ No newline at end of file diff --git a/packages/db/migrations/0400_public_share_entity_ownership.sql b/packages/db/migrations/0400_public_share_entity_ownership.sql new file mode 100644 index 00000000000..dbacde95e50 --- /dev/null +++ b/packages/db/migrations/0400_public_share_entity_ownership.sql @@ -0,0 +1,104 @@ +ALTER TABLE "public_share" ALTER COLUMN "workspace_id" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "public_share" ADD COLUMN IF NOT EXISTS "entity_type" text;--> statement-breakpoint +ALTER TABLE "public_share" ADD COLUMN IF NOT EXISTS "entity_id" text;--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'public_share_entity_binding_check' AND conrelid = 'public_share'::regclass) THEN +ALTER TABLE "public_share" ADD CONSTRAINT "public_share_entity_binding_check" CHECK ( + ("public_share"."entity_type" IS NULL AND "public_share"."entity_id" IS NULL AND "public_share"."workspace_id" IS NOT NULL) + OR ("public_share"."entity_type" IS NOT NULL AND "public_share"."entity_id" IS NOT NULL AND char_length("public_share"."entity_id") > 0 + AND (("public_share"."entity_type" = 'workspace' AND "public_share"."workspace_id" IS NOT NULL AND "public_share"."entity_id" = "public_share"."workspace_id") + OR ("public_share"."entity_type" = 'project' AND "public_share"."workspace_id" IS NULL AND "public_share"."resource_type" = 'file'))) + ) NOT VALID; + END IF; +END $$;--> statement-breakpoint +CREATE OR REPLACE FUNCTION public_share_sync_entity_binding() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE + target workspace_files%ROWTYPE; + target_type text; + target_id text; +BEGIN + IF TG_OP = 'UPDATE' THEN + IF ROW(NEW.resource_type, NEW.resource_id, NEW.workspace_id) + IS DISTINCT FROM ROW(OLD.resource_type, OLD.resource_id, OLD.workspace_id) + OR (OLD.entity_type IS NOT NULL AND ROW(NEW.entity_type, NEW.entity_id) + IS DISTINCT FROM ROW(OLD.entity_type, OLD.entity_id)) THEN + RAISE EXCEPTION 'A public share cannot change its resource or owner' USING ERRCODE = '23514'; + END IF; + END IF; + IF NEW.entity_type IS NULL AND NEW.entity_id IS NULL AND NEW.workspace_id IS NOT NULL THEN + NEW.entity_type := 'workspace'; + NEW.entity_id := NEW.workspace_id; + END IF; + IF NOT coalesce( + (NEW.entity_type = 'workspace' AND NEW.workspace_id IS NOT NULL AND NEW.entity_id = NEW.workspace_id) + OR (NEW.entity_type = 'project' AND NEW.entity_id <> '' AND NEW.workspace_id IS NULL AND NEW.resource_type = 'file'), + false + ) THEN + RAISE EXCEPTION 'Public share requires a compatible workspace or Project owner' USING ERRCODE = '23514'; + END IF; + IF NEW.resource_type = 'file' THEN + -- A parent row version also fences deletion from an older REPEATABLE READ snapshot. + UPDATE workspace_files SET id = id WHERE id = NEW.resource_id RETURNING * INTO target; + IF NOT FOUND THEN + RAISE EXCEPTION 'Public share file does not exist' USING ERRCODE = '23503'; + END IF; + target_type := target.entity_type; + target_id := target.entity_id; + IF target_type IS NULL AND target_id IS NULL THEN + SELECT entity_type, entity_id INTO target_type, target_id + FROM workspace_file_legacy_entity(target.context, target.workspace_id, target.organization_id, target.user_id); + END IF; + IF ROW(NEW.entity_type, NEW.entity_id) IS DISTINCT FROM ROW(target_type, target_id) THEN + RAISE EXCEPTION 'Public share file belongs to a different or unknown owner' USING ERRCODE = '23514'; + END IF; + END IF; + RETURN NEW; +END; +$$;--> statement-breakpoint +DROP TRIGGER IF EXISTS public_share_entity_binding ON public_share;--> statement-breakpoint +-- Configuration updates do not reacquire the parent after locking the share row. +CREATE TRIGGER public_share_entity_binding +BEFORE INSERT OR UPDATE OF resource_type, resource_id, workspace_id, entity_type, entity_id +ON public_share FOR EACH ROW EXECUTE FUNCTION public_share_sync_entity_binding();--> statement-breakpoint +CREATE OR REPLACE FUNCTION workspace_file_retire_public_shares() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE + previous_type text; + previous_id text; + current_type text; + current_id text; +BEGIN + IF TG_OP = 'DELETE' THEN + DELETE FROM public_share WHERE resource_type = 'file' AND resource_id = OLD.id; + RETURN OLD; + END IF; + previous_type := OLD.entity_type; + previous_id := OLD.entity_id; + current_type := NEW.entity_type; + current_id := NEW.entity_id; + IF previous_type IS NULL AND previous_id IS NULL THEN + SELECT entity_type, entity_id INTO previous_type, previous_id + FROM workspace_file_legacy_entity(OLD.context, OLD.workspace_id, OLD.organization_id, OLD.user_id); + END IF; + IF current_type IS NULL AND current_id IS NULL THEN + SELECT entity_type, entity_id INTO current_type, current_id + FROM workspace_file_legacy_entity(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id); + END IF; + IF NEW.id IS DISTINCT FROM OLD.id + OR ROW(previous_type, previous_id) IS DISTINCT FROM ROW(current_type, current_id) THEN + DELETE FROM public_share WHERE resource_type = 'file' AND resource_id = OLD.id; + END IF; + RETURN NEW; +END; +$$;--> statement-breakpoint +DROP TRIGGER IF EXISTS workspace_file_retire_public_shares ON workspace_files;--> statement-breakpoint +CREATE TRIGGER workspace_file_retire_public_shares +AFTER DELETE OR UPDATE OF id, entity_type, entity_id, workspace_id, organization_id, user_id, context +ON workspace_files FOR EACH ROW EXECUTE FUNCTION workspace_file_retire_public_shares();--> statement-breakpoint +COMMIT;--> statement-breakpoint +SET lock_timeout = 0;--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "public_share_entity_id_idx";--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "public_share_entity_id_idx" ON "public_share" USING btree ("entity_type","entity_id","resource_type"); +--> statement-breakpoint +SET lock_timeout = '5s'; diff --git a/packages/db/migrations/0401_file_search_owner_scope.sql b/packages/db/migrations/0401_file_search_owner_scope.sql new file mode 100644 index 00000000000..cbe5f52037f --- /dev/null +++ b/packages/db/migrations/0401_file_search_owner_scope.sql @@ -0,0 +1,168 @@ +CREATE TABLE IF NOT EXISTS "file_search_dependency" ( + "build_id" text NOT NULL, + "file_id" text NOT NULL, + "key" text NOT NULL, + "source_content_updated_at" timestamp NOT NULL, + CONSTRAINT "file_search_dependency_build_id_file_id_pk" PRIMARY KEY("build_id","file_id") +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "file_search_dispatch_queue" ( + "entity_type" text NOT NULL, + "entity_id" text NOT NULL, + "enqueued_at" timestamp DEFAULT now() NOT NULL, + "last_dispatched_at" timestamp, + "updated_at" timestamp DEFAULT now() NOT NULL, + CONSTRAINT "file_search_dispatch_queue_entity_type_entity_id_pk" PRIMARY KEY("entity_type","entity_id"), + CONSTRAINT "file_search_dispatch_owner_check" CHECK ("file_search_dispatch_queue"."entity_type" IN ('workspace', 'project') AND length("file_search_dispatch_queue"."entity_id") > 0) +); +--> statement-breakpoint +ALTER TABLE "workspace_file_search_build" ALTER COLUMN "workspace_id" DROP NOT NULL; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_chunk" ALTER COLUMN "workspace_id" DROP NOT NULL; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_revision" ALTER COLUMN "workspace_id" DROP NOT NULL; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_backfill" ADD COLUMN IF NOT EXISTS "after_entity_type" text; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_backfill" ADD COLUMN IF NOT EXISTS "after_entity_id" text; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_build" ADD COLUMN IF NOT EXISTS "artifact_key" text; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_build" ADD COLUMN IF NOT EXISTS "entity_type" text; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_build" ADD COLUMN IF NOT EXISTS "entity_id" text; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_chunk" ADD COLUMN IF NOT EXISTS "entity_type" text; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_chunk" ADD COLUMN IF NOT EXISTS "entity_id" text; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_revision" ADD COLUMN IF NOT EXISTS "entity_type" text; +--> statement-breakpoint +ALTER TABLE "workspace_file_search_revision" ADD COLUMN IF NOT EXISTS "entity_id" text; +--> statement-breakpoint +DO $$ BEGIN +IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'file_search_dependency_build_id_workspace_file_search_build_id_fk' AND conrelid = 'file_search_dependency'::regclass) THEN +ALTER TABLE "file_search_dependency" ADD CONSTRAINT "file_search_dependency_build_id_workspace_file_search_build_id_fk" FOREIGN KEY ("build_id") REFERENCES "public"."workspace_file_search_build"("id") ON DELETE cascade ON UPDATE no action; +END IF; +END $$; +--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "file_search_dependency_file_idx" ON "file_search_dependency" USING btree ("file_id","build_id"); +--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "file_search_dispatch_schedule_idx" ON "file_search_dispatch_queue" USING btree ("last_dispatched_at" NULLS FIRST,"enqueued_at","entity_type","entity_id"); +--> statement-breakpoint +DO $$ BEGIN +IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'file_search_build_owner_check' AND conrelid = 'workspace_file_search_build'::regclass) THEN +ALTER TABLE "workspace_file_search_build" ADD CONSTRAINT "file_search_build_owner_check" CHECK (("workspace_file_search_build"."entity_type" IS NULL AND "workspace_file_search_build"."entity_id" IS NULL AND "workspace_file_search_build"."workspace_id" IS NOT NULL) OR ("workspace_file_search_build"."entity_type" = 'workspace' AND "workspace_file_search_build"."entity_id" = "workspace_file_search_build"."workspace_id") OR ("workspace_file_search_build"."entity_type" = 'project' AND length("workspace_file_search_build"."entity_id") > 0 AND "workspace_file_search_build"."workspace_id" IS NULL)) NOT VALID; +END IF; +END $$; +--> statement-breakpoint +DO $$ BEGIN +IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'file_search_chunk_owner_check' AND conrelid = 'workspace_file_search_chunk'::regclass) THEN +ALTER TABLE "workspace_file_search_chunk" ADD CONSTRAINT "file_search_chunk_owner_check" CHECK (("workspace_file_search_chunk"."entity_type" IS NULL AND "workspace_file_search_chunk"."entity_id" IS NULL AND "workspace_file_search_chunk"."workspace_id" IS NOT NULL) OR ("workspace_file_search_chunk"."entity_type" = 'workspace' AND "workspace_file_search_chunk"."entity_id" = "workspace_file_search_chunk"."workspace_id") OR ("workspace_file_search_chunk"."entity_type" = 'project' AND length("workspace_file_search_chunk"."entity_id") > 0 AND "workspace_file_search_chunk"."workspace_id" IS NULL)) NOT VALID; +END IF; +END $$; +--> statement-breakpoint +DO $$ BEGIN +IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'file_search_revision_owner_check' AND conrelid = 'workspace_file_search_revision'::regclass) THEN +ALTER TABLE "workspace_file_search_revision" ADD CONSTRAINT "file_search_revision_owner_check" CHECK (("workspace_file_search_revision"."entity_type" IS NULL AND "workspace_file_search_revision"."entity_id" IS NULL AND "workspace_file_search_revision"."workspace_id" IS NOT NULL) OR ("workspace_file_search_revision"."entity_type" = 'workspace' AND "workspace_file_search_revision"."entity_id" = "workspace_file_search_revision"."workspace_id") OR ("workspace_file_search_revision"."entity_type" = 'project' AND length("workspace_file_search_revision"."entity_id") > 0 AND "workspace_file_search_revision"."workspace_id" IS NULL)) NOT VALID; +END IF; +END $$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION file_search_bind_owner() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE target_type text; target_id text; +BEGIN + IF NEW.entity_type IS NULL AND NEW.entity_id IS NULL AND NEW.workspace_id IS NOT NULL THEN + NEW.entity_type := 'workspace'; NEW.entity_id := NEW.workspace_id; + END IF; + IF NOT coalesce((NEW.entity_type = 'workspace' AND NEW.workspace_id IS NOT NULL AND NEW.entity_id = NEW.workspace_id) + OR (NEW.entity_type = 'project' AND NEW.workspace_id IS NULL AND length(NEW.entity_id) > 0), false) THEN + RAISE EXCEPTION 'File search requires a supported canonical owner' USING ERRCODE = '23514'; + END IF; + IF TG_TABLE_NAME = 'workspace_file_search_chunk' THEN + SELECT coalesce(entity_type, 'workspace'), coalesce(entity_id, workspace_id) + INTO target_type, target_id FROM workspace_file_search_build WHERE id = NEW.build_id; + ELSE + SELECT coalesce(entity_type, 'workspace'), coalesce(entity_id, workspace_id) + INTO target_type, target_id FROM workspace_files + WHERE id = NEW.file_id AND context IN ('workspace', 'project') FOR SHARE; + END IF; + IF NOT FOUND OR ROW(NEW.entity_type, NEW.entity_id) IS DISTINCT FROM ROW(target_type, target_id) THEN + RAISE EXCEPTION 'File search scope does not match its canonical parent' USING ERRCODE = '23514'; + END IF; + RETURN NEW; +END; +$$; +--> statement-breakpoint +DROP TRIGGER IF EXISTS file_search_build_owner ON workspace_file_search_build; +--> statement-breakpoint +CREATE TRIGGER file_search_build_owner BEFORE INSERT OR UPDATE OF entity_type, entity_id, workspace_id, file_id +ON workspace_file_search_build FOR EACH ROW EXECUTE FUNCTION file_search_bind_owner(); +--> statement-breakpoint +DROP TRIGGER IF EXISTS file_search_revision_owner ON workspace_file_search_revision; +--> statement-breakpoint +CREATE TRIGGER file_search_revision_owner BEFORE INSERT OR UPDATE OF entity_type, entity_id, workspace_id, file_id +ON workspace_file_search_revision FOR EACH ROW EXECUTE FUNCTION file_search_bind_owner(); +--> statement-breakpoint +DROP TRIGGER IF EXISTS file_search_chunk_owner ON workspace_file_search_chunk; +--> statement-breakpoint +CREATE TRIGGER file_search_chunk_owner BEFORE INSERT OR UPDATE OF entity_type, entity_id, workspace_id, build_id +ON workspace_file_search_chunk FOR EACH ROW EXECUTE FUNCTION file_search_bind_owner(); +--> statement-breakpoint +CREATE OR REPLACE FUNCTION workspace_file_search_mark_pending() +RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE owner_type text; owner_id text; +BEGIN + IF TG_OP = 'DELETE' THEN + UPDATE workspace_file_search_build SET expires_at = now() + WHERE id = (SELECT build_id FROM workspace_file_search_revision WHERE file_id = OLD.id); + owner_type := coalesce(OLD.entity_type, CASE WHEN OLD.context = 'workspace' THEN 'workspace' END); + owner_id := coalesce(OLD.entity_id, OLD.workspace_id); + IF owner_type IN ('workspace', 'project') AND OLD.context = owner_type AND owner_id IS NOT NULL THEN + INSERT INTO file_search_dispatch_queue (entity_type, entity_id) VALUES (owner_type, owner_id) + ON CONFLICT (entity_type, entity_id) DO UPDATE SET updated_at = now(); + END IF; + RETURN OLD; + END IF; + IF TG_OP = 'UPDATE' THEN + IF ROW(NEW.content_updated_at, NEW.deleted_at, NEW.context, NEW.workspace_id, NEW.entity_type, NEW.entity_id) + IS NOT DISTINCT FROM ROW(OLD.content_updated_at, OLD.deleted_at, OLD.context, OLD.workspace_id, OLD.entity_type, OLD.entity_id) THEN + RETURN NEW; + END IF; + UPDATE workspace_file_search_build SET expires_at = now() + WHERE id = (SELECT build_id FROM workspace_file_search_revision WHERE file_id = NEW.id); + DELETE FROM workspace_file_search_revision WHERE file_id = NEW.id; + END IF; + owner_type := coalesce(NEW.entity_type, CASE WHEN NEW.context = 'workspace' THEN 'workspace' END); + owner_id := coalesce(NEW.entity_id, NEW.workspace_id); + IF owner_type IN ('workspace', 'project') AND NEW.context = owner_type AND owner_id IS NOT NULL THEN + -- Waking the owner also reconciles bounded dependent builds without rewriting their sources here. + INSERT INTO file_search_dispatch_queue (entity_type, entity_id) VALUES (owner_type, owner_id) + ON CONFLICT (entity_type, entity_id) DO UPDATE SET updated_at = now(); + IF NEW.deleted_at IS NULL THEN + INSERT INTO workspace_file_search_revision (file_id, workspace_id, entity_type, entity_id, source_content_updated_at) + VALUES (NEW.id, NEW.workspace_id, owner_type, owner_id, NEW.content_updated_at) + ON CONFLICT (file_id) DO NOTHING; + IF owner_type = 'workspace' THEN + INSERT INTO workspace_file_search_dispatch_queue (workspace_id, enqueued_at, updated_at) + VALUES (owner_id, now(), now()) ON CONFLICT (workspace_id) DO UPDATE SET updated_at = now(); + END IF; + END IF; + END IF; + RETURN NEW; +END; +$$; +--> statement-breakpoint +COMMIT; +--> statement-breakpoint +SET lock_timeout = 0; +--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "file_search_chunk_owner_content_idx" ON "workspace_file_search_chunk" USING gin ("entity_type" text_ops,"entity_id" text_ops,"content" gin_trgm_ops) WITH (fastupdate=off) WHERE "workspace_file_search_chunk"."entity_type" = 'project'; +--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "file_search_revision_owner_pending_idx" ON "workspace_file_search_revision" USING btree (coalesce("entity_type", 'workspace'),coalesce("entity_id", "workspace_id"),"updated_at","file_id","source_content_updated_at") WHERE "workspace_file_search_revision"."status" = 'pending' AND "workspace_file_search_revision"."dispatched_at" IS NULL; +--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "file_search_revision_owner_status_idx" ON "workspace_file_search_revision" USING btree (coalesce("entity_type", 'workspace'),coalesce("entity_id", "workspace_id"),"status","dispatched_at"); +--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_search_owner_keyset_idx" ON "workspace_files" USING btree (coalesce("entity_type", 'workspace'),coalesce("entity_id", "workspace_id"),"id") WHERE "workspace_files"."deleted_at" IS NULL AND (("workspace_files"."context" = 'workspace' AND "workspace_files"."workspace_id" IS NOT NULL) OR ("workspace_files"."context" = 'project' AND "workspace_files"."entity_type" = 'project' AND "workspace_files"."entity_id" IS NOT NULL)); +--> statement-breakpoint +SET lock_timeout = '5s'; diff --git a/packages/db/migrations/meta/0396_snapshot.json b/packages/db/migrations/meta/0396_snapshot.json new file mode 100644 index 00000000000..257db0f0a22 --- /dev/null +++ b/packages/db/migrations/meta/0396_snapshot.json @@ -0,0 +1,29987 @@ +{ + "id": "d54d271f-dbfb-4b1e-b3f7-2717fbb8d078", + "prevId": "4120041c-55f9-462f-b56c-7dfbd9eb7112", + "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.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 + }, + "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": "'{}'" + }, + "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": {} + } + }, + "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" + } + }, + "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.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_search": { + "name": "embedding_keyword_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 + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "embedding_keyword_search_kb_idx": { + "name": "embedding_keyword_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_document_idx": { + "name": "embedding_keyword_search_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_content_idx": { + "name": "embedding_keyword_search_content_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_search_id_embedding_id_fk": { + "name": "embedding_keyword_search_id_embedding_id_fk", + "tableFrom": "embedding_keyword_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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_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.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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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_entity_id_idx": { + "name": "workspace_files_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "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_entity_binding_check": { + "name": "workspace_files_entity_binding_check", + "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" + }, + "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" + ] + }, + "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": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/0397_snapshot.json b/packages/db/migrations/meta/0397_snapshot.json new file mode 100644 index 00000000000..dc25d6c01fa --- /dev/null +++ b/packages/db/migrations/meta/0397_snapshot.json @@ -0,0 +1,30105 @@ +{ + "id": "2b2df413-017e-4a63-8dc6-58cb5c4188a6", + "prevId": "d54d271f-dbfb-4b1e-b3f7-2717fbb8d078", + "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.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 + }, + "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": "'{}'" + }, + "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": {} + } + }, + "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" + } + }, + "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.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_search": { + "name": "embedding_keyword_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 + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "embedding_keyword_search_kb_idx": { + "name": "embedding_keyword_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_document_idx": { + "name": "embedding_keyword_search_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_content_idx": { + "name": "embedding_keyword_search_content_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_search_id_embedding_id_fk": { + "name": "embedding_keyword_search_id_embedding_id_fk", + "tableFrom": "embedding_keyword_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": false + }, + "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_entity_id_idx": { + "name": "folder_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_entity_resource_parent_name_active_unique": { + "name": "folder_entity_resource_parent_name_active_unique", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_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": true, + "method": "btree", + "with": {} + }, + "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": { + "folder_entity_binding_check": { + "name": "folder_entity_binding_check", + "value": "(\"folder\".\"entity_type\" IS NULL AND \"folder\".\"entity_id\" IS NULL AND \"folder\".\"workspace_id\" IS NOT NULL) OR (\"folder\".\"entity_type\" IS NOT NULL AND \"folder\".\"entity_id\" IS NOT NULL AND char_length(\"folder\".\"entity_id\") > 0 AND ((\"folder\".\"entity_type\" = 'workspace' AND \"folder\".\"workspace_id\" IS NOT NULL AND \"folder\".\"entity_id\" = \"folder\".\"workspace_id\") OR (\"folder\".\"entity_type\" = 'project' AND \"folder\".\"workspace_id\" IS NULL AND \"folder\".\"resource_type\" = 'file')))" + } + }, + "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_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.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": false + }, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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_entity_id_idx": { + "name": "workspace_files_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_entity_folder_name_active_unique": { + "name": "workspace_files_entity_folder_name_active_unique", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_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\" IN ('workspace', 'project')", + "concurrently": true, + "method": "btree", + "with": {} + }, + "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_entity_binding_check": { + "name": "workspace_files_entity_binding_check", + "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" + }, + "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" + ] + }, + "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": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/0398_snapshot.json b/packages/db/migrations/meta/0398_snapshot.json new file mode 100644 index 00000000000..a06a9efaa60 --- /dev/null +++ b/packages/db/migrations/meta/0398_snapshot.json @@ -0,0 +1,30125 @@ +{ + "id": "007d0d81-be3a-443a-a49b-d7c1317b43ec", + "prevId": "2b2df413-017e-4a63-8dc6-58cb5c4188a6", + "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.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 + }, + "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": "'{}'" + }, + "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": {} + } + }, + "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" + } + }, + "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.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_search": { + "name": "embedding_keyword_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 + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "embedding_keyword_search_kb_idx": { + "name": "embedding_keyword_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_document_idx": { + "name": "embedding_keyword_search_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_content_idx": { + "name": "embedding_keyword_search_content_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_search_id_embedding_id_fk": { + "name": "embedding_keyword_search_id_embedding_id_fk", + "tableFrom": "embedding_keyword_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": false + }, + "original_creator_user_id": { + "name": "original_creator_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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_entity_id_idx": { + "name": "folder_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_entity_resource_parent_name_active_unique": { + "name": "folder_entity_resource_parent_name_active_unique", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_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": true, + "method": "btree", + "with": {} + }, + "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": { + "folder_creator_lifetime_check": { + "name": "folder_creator_lifetime_check", + "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"entity_type\" = 'project' AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + }, + "folder_entity_binding_check": { + "name": "folder_entity_binding_check", + "value": "(\"folder\".\"entity_type\" IS NULL AND \"folder\".\"entity_id\" IS NULL AND \"folder\".\"workspace_id\" IS NOT NULL) OR (\"folder\".\"entity_type\" IS NOT NULL AND \"folder\".\"entity_id\" IS NOT NULL AND char_length(\"folder\".\"entity_id\") > 0 AND ((\"folder\".\"entity_type\" = 'workspace' AND \"folder\".\"workspace_id\" IS NOT NULL AND \"folder\".\"entity_id\" = \"folder\".\"workspace_id\") OR (\"folder\".\"entity_type\" = 'project' AND \"folder\".\"workspace_id\" IS NULL AND \"folder\".\"resource_type\" = 'file')))" + } + }, + "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_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.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": false + }, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_creator_user_id": { + "name": "original_creator_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 + }, + "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_entity_id_idx": { + "name": "workspace_files_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_entity_folder_name_active_unique": { + "name": "workspace_files_entity_folder_name_active_unique", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_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\" IN ('workspace', 'project')", + "concurrently": true, + "method": "btree", + "with": {} + }, + "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_creator_lifetime_check": { + "name": "workspace_files_creator_lifetime_check", + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"entity_type\" = 'project' AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" + }, + "workspace_files_entity_binding_check": { + "name": "workspace_files_entity_binding_check", + "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" + }, + "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" + ] + }, + "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": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/0399_snapshot.json b/packages/db/migrations/meta/0399_snapshot.json new file mode 100644 index 00000000000..b98f05a3e0b --- /dev/null +++ b/packages/db/migrations/meta/0399_snapshot.json @@ -0,0 +1,30126 @@ +{ + "id": "9df5563b-fff7-4d4c-8774-aec5cbd2ebfe", + "prevId": "007d0d81-be3a-443a-a49b-d7c1317b43ec", + "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.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 + }, + "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": "'{}'" + }, + "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": {} + } + }, + "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" + } + }, + "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.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_search": { + "name": "embedding_keyword_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 + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "embedding_keyword_search_kb_idx": { + "name": "embedding_keyword_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_document_idx": { + "name": "embedding_keyword_search_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_content_idx": { + "name": "embedding_keyword_search_content_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_search_id_embedding_id_fk": { + "name": "embedding_keyword_search_id_embedding_id_fk", + "tableFrom": "embedding_keyword_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": false + }, + "original_creator_user_id": { + "name": "original_creator_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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_entity_id_idx": { + "name": "folder_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_entity_resource_parent_name_active_unique": { + "name": "folder_entity_resource_parent_name_active_unique", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_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": true, + "method": "btree", + "with": {} + }, + "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": { + "folder_creator_lifetime_check": { + "name": "folder_creator_lifetime_check", + "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"entity_type\" = 'project' AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + }, + "folder_entity_binding_check": { + "name": "folder_entity_binding_check", + "value": "(\"folder\".\"entity_type\" IS NULL AND \"folder\".\"entity_id\" IS NULL AND \"folder\".\"workspace_id\" IS NOT NULL) OR (\"folder\".\"entity_type\" IS NOT NULL AND \"folder\".\"entity_id\" IS NOT NULL AND char_length(\"folder\".\"entity_id\") > 0 AND ((\"folder\".\"entity_type\" = 'workspace' AND \"folder\".\"workspace_id\" IS NOT NULL AND \"folder\".\"entity_id\" = \"folder\".\"workspace_id\") OR (\"folder\".\"entity_type\" = 'project' AND \"folder\".\"workspace_id\" IS NULL AND \"folder\".\"resource_type\" = 'file')))" + } + }, + "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_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.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": false + }, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_creator_user_id": { + "name": "original_creator_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 + }, + "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_entity_id_idx": { + "name": "workspace_files_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_entity_folder_name_active_unique": { + "name": "workspace_files_entity_folder_name_active_unique", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_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\" IN ('workspace', 'project')", + "concurrently": true, + "method": "btree", + "with": {} + }, + "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_creator_lifetime_check": { + "name": "workspace_files_creator_lifetime_check", + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"entity_type\" = 'project' AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" + }, + "workspace_files_entity_binding_check": { + "name": "workspace_files_entity_binding_check", + "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" + }, + "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", + "project_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" + ] + }, + "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": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/0400_snapshot.json b/packages/db/migrations/meta/0400_snapshot.json new file mode 100644 index 00000000000..34542b6572e --- /dev/null +++ b/packages/db/migrations/meta/0400_snapshot.json @@ -0,0 +1,30170 @@ +{ + "id": "6127dc99-6e8f-4842-9a28-7805522e406d", + "prevId": "9df5563b-fff7-4d4c-8774-aec5cbd2ebfe", + "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.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 + }, + "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": "'{}'" + }, + "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": {} + } + }, + "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" + } + }, + "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.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_search": { + "name": "embedding_keyword_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 + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "embedding_keyword_search_kb_idx": { + "name": "embedding_keyword_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_document_idx": { + "name": "embedding_keyword_search_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_content_idx": { + "name": "embedding_keyword_search_content_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_search_id_embedding_id_fk": { + "name": "embedding_keyword_search_id_embedding_id_fk", + "tableFrom": "embedding_keyword_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": false + }, + "original_creator_user_id": { + "name": "original_creator_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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_entity_id_idx": { + "name": "folder_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_entity_resource_parent_name_active_unique": { + "name": "folder_entity_resource_parent_name_active_unique", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_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": true, + "method": "btree", + "with": {} + }, + "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": { + "folder_creator_lifetime_check": { + "name": "folder_creator_lifetime_check", + "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"entity_type\" = 'project' AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + }, + "folder_entity_binding_check": { + "name": "folder_entity_binding_check", + "value": "(\"folder\".\"entity_type\" IS NULL AND \"folder\".\"entity_id\" IS NULL AND \"folder\".\"workspace_id\" IS NOT NULL) OR (\"folder\".\"entity_type\" IS NOT NULL AND \"folder\".\"entity_id\" IS NOT NULL AND char_length(\"folder\".\"entity_id\") > 0 AND ((\"folder\".\"entity_type\" = 'workspace' AND \"folder\".\"workspace_id\" IS NOT NULL AND \"folder\".\"entity_id\" = \"folder\".\"workspace_id\") OR (\"folder\".\"entity_type\" = 'project' AND \"folder\".\"workspace_id\" IS NULL AND \"folder\".\"resource_type\" = 'file')))" + } + }, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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_entity_id_idx": { + "name": "public_share_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "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": { + "public_share_entity_binding_check": { + "name": "public_share_entity_binding_check", + "value": "\n (\"public_share\".\"entity_type\" IS NULL AND \"public_share\".\"entity_id\" IS NULL AND \"public_share\".\"workspace_id\" IS NOT NULL)\n OR (\"public_share\".\"entity_type\" IS NOT NULL AND \"public_share\".\"entity_id\" IS NOT NULL AND char_length(\"public_share\".\"entity_id\") > 0\n AND ((\"public_share\".\"entity_type\" = 'workspace' AND \"public_share\".\"workspace_id\" IS NOT NULL AND \"public_share\".\"entity_id\" = \"public_share\".\"workspace_id\")\n OR (\"public_share\".\"entity_type\" = 'project' AND \"public_share\".\"workspace_id\" IS NULL AND \"public_share\".\"resource_type\" = 'file')))\n " + } + }, + "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_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.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": false + }, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_creator_user_id": { + "name": "original_creator_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 + }, + "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_entity_id_idx": { + "name": "workspace_files_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_entity_folder_name_active_unique": { + "name": "workspace_files_entity_folder_name_active_unique", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_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\" IN ('workspace', 'project')", + "concurrently": true, + "method": "btree", + "with": {} + }, + "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_creator_lifetime_check": { + "name": "workspace_files_creator_lifetime_check", + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"entity_type\" = 'project' AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" + }, + "workspace_files_entity_binding_check": { + "name": "workspace_files_entity_binding_check", + "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" + }, + "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", + "project_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" + ] + }, + "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": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/0401_snapshot.json b/packages/db/migrations/meta/0401_snapshot.json new file mode 100644 index 00000000000..4e64b2788c0 --- /dev/null +++ b/packages/db/migrations/meta/0401_snapshot.json @@ -0,0 +1,30535 @@ +{ + "id": "ec46a436-88d2-4e86-a5f5-87711de5b966", + "prevId": "6127dc99-6e8f-4842-9a28-7805522e406d", + "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.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 + }, + "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": "'{}'" + }, + "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": {} + } + }, + "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" + } + }, + "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.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_search": { + "name": "embedding_keyword_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 + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "embedding_keyword_search_kb_idx": { + "name": "embedding_keyword_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_document_idx": { + "name": "embedding_keyword_search_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_content_idx": { + "name": "embedding_keyword_search_content_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_search_id_embedding_id_fk": { + "name": "embedding_keyword_search_id_embedding_id_fk", + "tableFrom": "embedding_keyword_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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.file_search_dependency": { + "name": "file_search_dependency", + "schema": "", + "columns": { + "build_id": { + "name": "build_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "file_search_dependency_file_idx": { + "name": "file_search_dependency_file_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "build_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "file_search_dependency_build_id_workspace_file_search_build_id_fk": { + "name": "file_search_dependency_build_id_workspace_file_search_build_id_fk", + "tableFrom": "file_search_dependency", + "tableTo": "workspace_file_search_build", + "columnsFrom": ["build_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "file_search_dependency_build_id_file_id_pk": { + "name": "file_search_dependency_build_id_file_id_pk", + "columns": ["build_id", "file_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.file_search_dispatch_queue": { + "name": "file_search_dispatch_queue", + "schema": "", + "columns": { + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "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": { + "file_search_dispatch_schedule_idx": { + "name": "file_search_dispatch_schedule_idx", + "columns": [ + { + "expression": "last_dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "first" + }, + { + "expression": "enqueued_at", + "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": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "file_search_dispatch_queue_entity_type_entity_id_pk": { + "name": "file_search_dispatch_queue_entity_type_entity_id_pk", + "columns": ["entity_type", "entity_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "file_search_dispatch_owner_check": { + "name": "file_search_dispatch_owner_check", + "value": "\"file_search_dispatch_queue\".\"entity_type\" IN ('workspace', 'project') AND length(\"file_search_dispatch_queue\".\"entity_id\") > 0" + } + }, + "isRLSEnabled": false + }, + "public.folder": { + "name": "folder", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": false + }, + "original_creator_user_id": { + "name": "original_creator_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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_entity_id_idx": { + "name": "folder_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_entity_resource_parent_name_active_unique": { + "name": "folder_entity_resource_parent_name_active_unique", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_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": true, + "method": "btree", + "with": {} + }, + "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": { + "folder_creator_lifetime_check": { + "name": "folder_creator_lifetime_check", + "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"entity_type\" = 'project' AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + }, + "folder_entity_binding_check": { + "name": "folder_entity_binding_check", + "value": "(\"folder\".\"entity_type\" IS NULL AND \"folder\".\"entity_id\" IS NULL AND \"folder\".\"workspace_id\" IS NOT NULL) OR (\"folder\".\"entity_type\" IS NOT NULL AND \"folder\".\"entity_id\" IS NOT NULL AND char_length(\"folder\".\"entity_id\") > 0 AND ((\"folder\".\"entity_type\" = 'workspace' AND \"folder\".\"workspace_id\" IS NOT NULL AND \"folder\".\"entity_id\" = \"folder\".\"workspace_id\") OR (\"folder\".\"entity_type\" = 'project' AND \"folder\".\"workspace_id\" IS NULL AND \"folder\".\"resource_type\" = 'file')))" + } + }, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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_entity_id_idx": { + "name": "public_share_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "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": { + "public_share_entity_binding_check": { + "name": "public_share_entity_binding_check", + "value": "\n (\"public_share\".\"entity_type\" IS NULL AND \"public_share\".\"entity_id\" IS NULL AND \"public_share\".\"workspace_id\" IS NOT NULL)\n OR (\"public_share\".\"entity_type\" IS NOT NULL AND \"public_share\".\"entity_id\" IS NOT NULL AND char_length(\"public_share\".\"entity_id\") > 0\n AND ((\"public_share\".\"entity_type\" = 'workspace' AND \"public_share\".\"workspace_id\" IS NOT NULL AND \"public_share\".\"entity_id\" = \"public_share\".\"workspace_id\")\n OR (\"public_share\".\"entity_type\" = 'project' AND \"public_share\".\"workspace_id\" IS NULL AND \"public_share\".\"resource_type\" = 'file')))\n " + } + }, + "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_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.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_entity_type": { + "name": "after_entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "after_entity_id": { + "name": "after_entity_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": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "artifact_key": { + "name": "artifact_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": { + "file_search_build_owner_check": { + "name": "file_search_build_owner_check", + "value": "(\"workspace_file_search_build\".\"entity_type\" IS NULL AND \"workspace_file_search_build\".\"entity_id\" IS NULL AND \"workspace_file_search_build\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_build\".\"entity_type\" = 'workspace' AND \"workspace_file_search_build\".\"entity_id\" = \"workspace_file_search_build\".\"workspace_id\") OR (\"workspace_file_search_build\".\"entity_type\" = 'project' AND length(\"workspace_file_search_build\".\"entity_id\") > 0 AND \"workspace_file_search_build\".\"workspace_id\" IS NULL)" + } + }, + "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": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": { + "file_search_chunk_owner_content_idx": { + "name": "file_search_chunk_owner_content_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "content", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_chunk\".\"entity_type\" = 'project'", + "concurrently": true, + "method": "gin", + "with": { + "fastupdate": "off" + } + }, + "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": { + "file_search_chunk_owner_check": { + "name": "file_search_chunk_owner_check", + "value": "(\"workspace_file_search_chunk\".\"entity_type\" IS NULL AND \"workspace_file_search_chunk\".\"entity_id\" IS NULL AND \"workspace_file_search_chunk\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_chunk\".\"entity_type\" = 'workspace' AND \"workspace_file_search_chunk\".\"entity_id\" = \"workspace_file_search_chunk\".\"workspace_id\") OR (\"workspace_file_search_chunk\".\"entity_type\" = 'project' AND length(\"workspace_file_search_chunk\".\"entity_id\") > 0 AND \"workspace_file_search_chunk\".\"workspace_id\" IS NULL)" + }, + "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": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": { + "file_search_revision_owner_pending_idx": { + "name": "file_search_revision_owner_pending_idx", + "columns": [ + { + "expression": "coalesce(\"entity_type\", 'workspace')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"entity_id\", \"workspace_id\")", + "asc": true, + "isExpression": 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": true, + "method": "btree", + "with": {} + }, + "file_search_revision_owner_status_idx": { + "name": "file_search_revision_owner_status_idx", + "columns": [ + { + "expression": "coalesce(\"entity_type\", 'workspace')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"entity_id\", \"workspace_id\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "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": { + "file_search_revision_owner_check": { + "name": "file_search_revision_owner_check", + "value": "(\"workspace_file_search_revision\".\"entity_type\" IS NULL AND \"workspace_file_search_revision\".\"entity_id\" IS NULL AND \"workspace_file_search_revision\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_revision\".\"entity_type\" = 'workspace' AND \"workspace_file_search_revision\".\"entity_id\" = \"workspace_file_search_revision\".\"workspace_id\") OR (\"workspace_file_search_revision\".\"entity_type\" = 'project' AND length(\"workspace_file_search_revision\".\"entity_id\") > 0 AND \"workspace_file_search_revision\".\"workspace_id\" IS NULL)" + } + }, + "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": false + }, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_creator_user_id": { + "name": "original_creator_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 + }, + "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_entity_id_idx": { + "name": "workspace_files_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_entity_folder_name_active_unique": { + "name": "workspace_files_entity_folder_name_active_unique", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_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\" IN ('workspace', 'project')", + "concurrently": true, + "method": "btree", + "with": {} + }, + "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_search_owner_keyset_idx": { + "name": "workspace_files_search_owner_keyset_idx", + "columns": [ + { + "expression": "coalesce(\"entity_type\", 'workspace')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"entity_id\", \"workspace_id\")", + "asc": true, + "isExpression": 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) OR (\"workspace_files\".\"context\" = 'project' AND \"workspace_files\".\"entity_type\" = 'project' AND \"workspace_files\".\"entity_id\" IS NOT NULL))", + "concurrently": true, + "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_creator_lifetime_check": { + "name": "workspace_files_creator_lifetime_check", + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"entity_type\" = 'project' AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" + }, + "workspace_files_entity_binding_check": { + "name": "workspace_files_entity_binding_check", + "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" + }, + "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", + "project_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" + ] + }, + "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": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json index 916f346be46..b7c1f60387f 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -2766,6 +2766,48 @@ "when": 1790986787652, "tag": "0395_project_membership_enforcement", "breakpoints": true + }, + { + "idx": 396, + "version": "7", + "when": 1791062868437, + "tag": "0396_file_entity_ownership", + "breakpoints": true + }, + { + "idx": 397, + "version": "7", + "when": 1791062887359, + "tag": "0397_file_folder_version_ownership", + "breakpoints": true + }, + { + "idx": 398, + "version": "7", + "when": 1791068073868, + "tag": "0398_file_creator_lifetime", + "breakpoints": true + }, + { + "idx": 399, + "version": "7", + "when": 1791071965882, + "tag": "0399_project_file_upload_sessions", + "breakpoints": true + }, + { + "idx": 400, + "version": "7", + "when": 1791075071615, + "tag": "0400_public_share_entity_ownership", + "breakpoints": true + }, + { + "idx": 401, + "version": "7", + "when": 1791091736313, + "tag": "0401_file_search_owner_scope", + "breakpoints": true } ] } diff --git a/packages/db/package.json b/packages/db/package.json index 31cf5145650..97d0f50f215 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -28,6 +28,10 @@ "./sso-primary-provider": { "types": "./sso-primary-provider.ts", "default": "./sso-primary-provider.ts" + }, + "./testing/test-infrastructure": { + "types": "./testing/test-infrastructure.ts", + "default": "./testing/test-infrastructure.ts" } }, "scripts": { diff --git a/packages/db/public-share-ownership.integration.ts b/packages/db/public-share-ownership.integration.ts new file mode 100644 index 00000000000..9cddf286587 --- /dev/null +++ b/packages/db/public-share-ownership.integration.ts @@ -0,0 +1,375 @@ +import { readFileSync } from 'node:fs' +import { mkdir, writeFile } from 'node:fs/promises' +import { dirname } from 'node:path' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { createDeferred } from '@sim/testing/helpers/deferred' +import { getErrorMessage } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import postgres, { type Sql } from 'postgres' +import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' + +const databaseUrl = readTestDatabaseUrl() +const migrations = [ + '0396_file_entity_ownership.sql', + '0397_file_folder_version_ownership.sql', + '0398_file_creator_lifetime.sql', + '0400_public_share_entity_ownership.sql', +].map((name) => readFileSync(new URL(`./migrations/${name}`, import.meta.url), 'utf8')) +const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = + [] +function check(name: string, run: () => Promise) { + it(name, async () => { + const started = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - started }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - started, + error: getErrorMessage(error), + }) + throw error + } + }, 10_000) +} + +describe('Public share canonical ownership in PostgreSQL', () => { + const schemaName = `public_share_owner_${generateId().replaceAll('-', '')}` + let sql: Sql + let admin: Sql + async function applyMigration(migration: string) { + for (const statement of migration.split('--> statement-breakpoint')) { + if (statement.trim()) await sql.unsafe(statement) + } + } + async function waitForDatabaseLock(pid: number) { + for (let attempt = 0; attempt < 100; attempt += 1) { + const [state] = await sql<{ waiting: boolean }[]>`SELECT EXISTS ( + SELECT 1 FROM pg_stat_activity WHERE pid = ${pid} AND wait_event_type = 'Lock' + ) AS waiting` + if (state.waiting) return true + await sleep(10) + } + return false + } + beforeAll(async () => { + admin = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) + await admin.unsafe(`CREATE SCHEMA "${schemaName}"`) + sql = postgres(databaseUrl, { + max: 3, + onnotice: () => undefined, + connection: { search_path: schemaName, TimeZone: 'UTC' }, + }) + await sql`CREATE TABLE "user" (id text PRIMARY KEY)` + await sql`CREATE TABLE workspace (id text PRIMARY KEY)` + await sql`CREATE TABLE organization (id text PRIMARY KEY)` + await sql`CREATE TABLE project ( + id text PRIMARY KEY, name text NOT NULL DEFAULT 'Project', + owner_id text NOT NULL DEFAULT 'user-b' REFERENCES "user"(id) ON DELETE RESTRICT, + updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456' + )` + await sql`CREATE TABLE folder ( + id text PRIMARY KEY, name text NOT NULL, + user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, + workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE, + resource_type text NOT NULL DEFAULT 'workflow', + parent_id text REFERENCES folder(id) ON DELETE SET NULL, + deleted_at timestamp + )` + await sql`CREATE UNIQUE INDEX folder_workspace_resource_parent_name_active_unique + ON folder(workspace_id, resource_type, coalesce(parent_id, ''), name) + WHERE deleted_at IS NULL` + await sql`CREATE TABLE workspace_files ( + id text PRIMARY KEY, context text NOT NULL, + user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, + workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, + organization_id text REFERENCES organization(id) ON DELETE CASCADE, + folder_id text REFERENCES folder(id) ON DELETE SET NULL, + chat_id text, original_name text NOT NULL DEFAULT 'document.md', + key text NOT NULL DEFAULT 'unchanged-object-key', deleted_at timestamp, + content_updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456', + secret_provenance_version integer DEFAULT 1 + )` + await sql`CREATE UNIQUE INDEX workspace_files_workspace_folder_name_active_unique + ON workspace_files(workspace_id, coalesce(folder_id, ''), original_name) + WHERE deleted_at IS NULL AND context = 'workspace'` + await sql`CREATE TABLE workspace_file_version ( + id text PRIMARY KEY, file_id text NOT NULL REFERENCES workspace_files(id) ON DELETE CASCADE, + workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE, + version integer NOT NULL DEFAULT 1, key text NOT NULL DEFAULT 'unchanged-version-key', + author_user_ids text[] NOT NULL DEFAULT '{}'::text[], UNIQUE(file_id, version) + )` + await sql`CREATE TABLE public_share ( + id text PRIMARY KEY, resource_type text NOT NULL, resource_id text NOT NULL, + workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE, + created_by text REFERENCES "user"(id) ON DELETE SET NULL, + token text NOT NULL UNIQUE, is_active boolean NOT NULL DEFAULT true, + updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456', + UNIQUE(resource_type, resource_id) + )` + for (const migration of migrations) await applyMigration(migration) + }) + + beforeEach(async () => { + await sql`TRUNCATE public_share, workspace_file_version, workspace_files, folder, project, workspace, organization, "user"` + await sql`INSERT INTO "user" VALUES ('user-a'), ('user-b')` + await sql`INSERT INTO workspace VALUES ('workspace-a'), ('workspace-b'), ('same-id')` + await sql`INSERT INTO organization VALUES ('organization-a')` + await sql`INSERT INTO project (id) VALUES ('project-a'), ('project-b'), ('same-id')` + }) + + afterAll(async () => { + try { + await sql?.end({ timeout: 1 }) + await admin?.unsafe(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`) + } finally { + await admin?.end({ timeout: 1 }) + const reportPath = process.env.PUBLIC_SHARE_OWNERSHIP_REPORT_PATH + if (reportPath) { + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) + } + } + }) + + async function file(id = 'file', owner = 'project-a', kind: 'project' | 'workspace' = 'project') { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, entity_type, entity_id, original_name) + VALUES (${id}, ${kind}, 'user-a', ${kind === 'workspace' ? owner : null}, ${kind}, ${owner}, ${id})` + } + function share( + executor: Sql, + id = 'share', + fileId = 'file', + owner = 'project-a', + kind: 'project' | 'workspace' = 'project' + ) { + return executor`INSERT INTO public_share (id, resource_type, resource_id, workspace_id, created_by, token, entity_type, entity_id) + VALUES (${id}, 'file', ${fileId}, ${kind === 'workspace' ? owner : null}, 'user-a', ${id}, ${kind}, ${owner})` + } + + check( + 'legacy file shares derive the exact workspace and reject a different target owner', + async () => { + await file('file', 'workspace-a', 'workspace') + await expect(sql`INSERT INTO public_share (id, resource_type, resource_id, workspace_id, token) + VALUES ('wrong', 'file', 'file', 'workspace-b', 'wrong')`).rejects.toMatchObject({ + code: '23514', + }) + await sql`INSERT INTO public_share (id, resource_type, resource_id, workspace_id, token) + VALUES ('legacy', 'file', 'file', 'workspace-a', 'legacy')` + expect(await sql`SELECT entity_type, entity_id FROM public_share`).toEqual([ + { entity_type: 'workspace', entity_id: 'workspace-a' }, + ]) + } + ) + + check( + 'Project shares reject foreign, missing and nonfile targets without borrowing workspace ownership', + async () => { + await file() + await expect(share(sql, 'wrong', 'file', 'project-b')).rejects.toMatchObject({ + code: '23514', + }) + await expect(share(sql, 'missing', 'missing')).rejects.toMatchObject({ code: '23503' }) + await expect(sql`INSERT INTO public_share (id, resource_type, resource_id, token, entity_type, entity_id) + VALUES ('folder', 'folder', 'file', 'folder', 'project', 'project-a')`).rejects.toMatchObject( + { code: '23514' } + ) + await share(sql) + await expect( + sql`UPDATE public_share SET entity_id = 'project-b' WHERE id = 'share'` + ).rejects.toMatchObject({ code: '23514' }) + await expect( + sql`UPDATE public_share SET resource_id = 'missing' WHERE id = 'share'` + ).rejects.toMatchObject({ code: '23514' }) + } + ) + + check( + 'creator deletion and archive restore preserve a token; hard deletion invalidates it', + async () => { + await file() + await share(sql) + await sql`DELETE FROM "user" WHERE id = 'user-a'` + expect(await sql`SELECT token, created_by FROM public_share`).toEqual([ + { token: 'share', created_by: null }, + ]) + await sql`UPDATE workspace_files SET deleted_at = now() WHERE id = 'file'` + await sql`UPDATE workspace_files SET deleted_at = NULL WHERE id = 'file'` + expect(await sql`SELECT token FROM public_share`).toEqual([{ token: 'share' }]) + await sql`DELETE FROM workspace_files WHERE id = 'file'` + expect(await sql`SELECT id FROM public_share`).toEqual([]) + } + ) + + check( + 'workspace owner changes invalidate existing links and preserve legacy nonfile shares', + async () => { + await file('file', 'workspace-a', 'workspace') + await share(sql, 'share', 'file', 'workspace-a', 'workspace') + await sql`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'file'` + expect(await sql`SELECT id FROM public_share`).toEqual([]) + await sql`INSERT INTO public_share (id, resource_type, resource_id, workspace_id, token) + VALUES ('legacy', 'folder', 'unregistered-legacy-target', 'workspace-a', 'legacy')` + expect(await sql`SELECT entity_type, entity_id FROM public_share`).toEqual([ + { entity_type: 'workspace', entity_id: 'workspace-a' }, + ]) + } + ) + + check( + 'bounded backfill stamps compatible owners, preserves share settings, and quarantines stale targets', + async () => { + await file('valid-file', 'workspace-a', 'workspace') + await file('foreign-file', 'workspace-b', 'workspace') + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('unknown-file', 'unreviewed-purpose', 'user-a', 'workspace-a')` + await sql`ALTER TABLE public_share DISABLE TRIGGER public_share_entity_binding` + try { + await sql`INSERT INTO public_share (id, resource_type, resource_id, workspace_id, token, is_active) + SELECT 'legacy-' || lpad(n::text, 4, '0'), 'folder', 'legacy-folder-' || n, + 'workspace-a', 'legacy-token-' || n, false FROM generate_series(1, 501) n` + await sql`INSERT INTO public_share (id, resource_type, resource_id, workspace_id, token) + VALUES ('valid', 'file', 'valid-file', 'workspace-a', 'valid-token'), + ('foreign', 'file', 'foreign-file', 'workspace-a', 'foreign-token'), + ('missing', 'file', 'missing-file', 'workspace-a', 'missing-token'), + ('unknown', 'file', 'unknown-file', 'workspace-a', 'unknown-token')` + } finally { + await sql`ALTER TABLE public_share ENABLE TRIGGER public_share_entity_binding` + } + const before = + await sql`SELECT id, resource_type, resource_id, workspace_id, token, is_active, updated_at FROM public_share ORDER BY id` + const { backfillPublicShareEntities } = await import( + './script-migrations/0033_backfill_public_share_entities' + ) + const first = await backfillPublicShareEntities(sql) + expect(first).toMatchObject({ backfilled: 502, unresolved: 3 }) + expect(first.sampleShareIds.sort()).toEqual(['foreign', 'missing', 'unknown']) + expect(await sql`SELECT id FROM public_share WHERE entity_type IS NULL ORDER BY id`).toEqual([ + { id: 'foreign' }, + { id: 'missing' }, + { id: 'unknown' }, + ]) + expect( + await sql`SELECT id, resource_type, resource_id, workspace_id, token, is_active, updated_at FROM public_share ORDER BY id` + ).toEqual(before) + expect(await backfillPublicShareEntities(sql)).toMatchObject({ backfilled: 0, unresolved: 3 }) + } + ) + + for (const isolation of ['read committed', 'repeatable read'] as const) { + check( + `${isolation}: file deletion waits for share insertion and cannot leave an orphan`, + async () => { + await file() + const snapshot = createDeferred() + const startDelete = createDeferred() + const pid = createDeferred() + const deleting = sql + .begin(`isolation level ${isolation}`, async (tx) => { + await tx`SELECT count(*) FROM public_share` + snapshot.resolve() + await startDelete.promise + const [connection] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + pid.resolve(connection.pid) + await tx`DELETE FROM workspace_files WHERE id = 'file'` + }) + .then( + () => null, + (error: unknown) => error + ) + await Promise.race([ + snapshot.promise, + deleting.then((error) => { + if (error) throw error + throw new Error('Deletion ended before acquiring its snapshot') + }), + ]) + const inserted = createDeferred() + const release = createDeferred() + const inserting = sql.begin(async (tx) => { + await tx`INSERT INTO public_share (id, resource_type, resource_id, token, entity_type, entity_id) + VALUES ('share', 'file', 'file', 'share', 'project', 'project-a')` + inserted.resolve() + await release.promise + }) + let waited = false + try { + await Promise.race([inserted.promise, inserting]) + startDelete.resolve() + const deletePid = await Promise.race([ + pid.promise, + deleting.then((error) => { + if (error) throw error + throw new Error('Deletion ended before publishing its connection') + }), + ]) + waited = await waitForDatabaseLock(deletePid) + } finally { + startDelete.resolve() + release.resolve() + await inserting + await deleting + } + expect(waited, 'target deletion must wait for share insertion').toBe(true) + const result = await deleting + expect( + await sql`SELECT share.id FROM public_share share + LEFT JOIN workspace_files file ON file.id = share.resource_id + WHERE share.resource_type = 'file' AND file.id IS NULL` + ).toEqual([]) + if (isolation === 'repeatable read') expect(result).toMatchObject({ code: '40001' }) + else expect(result).toBeNull() + await sql`DELETE FROM workspace_files WHERE id = 'file'` + expect(await sql`SELECT id FROM public_share`).toEqual([]) + } + ) + + check(`${isolation}: a new share cannot commit behind target deletion`, async () => { + await file() + const deleted = createDeferred() + const release = createDeferred() + const deleting = sql.begin(async (tx) => { + await tx`DELETE FROM workspace_files WHERE id = 'file'` + deleted.resolve() + await release.promise + }) + await Promise.race([deleted.promise, deleting]) + const pid = createDeferred() + const inserting = sql + .begin(`isolation level ${isolation}`, async (tx) => { + const [connection] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + pid.resolve(connection.pid) + await tx`INSERT INTO public_share (id, resource_type, resource_id, token, entity_type, entity_id) + VALUES ('share', 'file', 'file', 'share', 'project', 'project-a')` + }) + .then( + () => null, + (error: unknown) => error + ) + let waited = false + try { + const insertPid = await Promise.race([ + pid.promise, + inserting.then((error) => { + if (error) throw error + throw new Error('Insertion ended before publishing its connection') + }), + ]) + waited = await waitForDatabaseLock(insertPid) + } finally { + release.resolve() + await deleting + await inserting + } + expect(waited, 'share insertion must wait for target deletion').toBe(true) + expect(await inserting).toMatchObject({ + code: isolation === 'repeatable read' ? '40001' : '23503', + }) + expect(await sql`SELECT id FROM public_share`).toEqual([]) + }) + } +}) diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 91face47ad7..0d138cf517e 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -229,14 +229,17 @@ export const folder = pgTable( 'folder', { id: text('id').primaryKey(), + /** File folders may belong to a Project; other resource folders remain workspace-owned. */ + // contract-pending(after folder entity backfill and owner-aware readers are deployed): require both ownership fields. + entityType: text('entity_type'), + entityId: text('entity_id'), resourceType: folderResourceTypeEnum('resource_type').notNull(), name: text('name').notNull(), - userId: text('user_id') - .notNull() - .references(() => user.id, { onDelete: 'cascade' }), - workspaceId: text('workspace_id') - .notNull() - .references(() => workspace.id, { onDelete: 'cascade' }), + /** Project creator references clear on account deletion; legacy folders retain their cascade. */ + userId: text('user_id').references(() => user.id, { onDelete: 'cascade' }), + /** Immutable original Project creator, retained like file-version author IDs after account deletion. */ + originalCreatorUserId: text('original_creator_user_id'), + workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'cascade' }), parentId: text('parent_id').references((): AnyPgColumn => folder.id, { onDelete: 'set null', }), @@ -247,6 +250,29 @@ export const folder = pgTable( deletedAt: timestamp('deleted_at'), }, (table) => ({ + creatorLifetimeCheck: check( + 'folder_creator_lifetime_check', + sql`${table.userId} IS NOT NULL OR coalesce(${table.entityType} = 'project' AND char_length(${table.originalCreatorUserId}) > 0, false)` + ), + entityBindingCheck: check( + 'folder_entity_binding_check', + sql`(${table.entityType} IS NULL AND ${table.entityId} IS NULL AND ${table.workspaceId} IS NOT NULL) OR (${table.entityType} IS NOT NULL AND ${table.entityId} IS NOT NULL AND char_length(${table.entityId}) > 0 AND ((${table.entityType} = 'workspace' AND ${table.workspaceId} IS NOT NULL AND ${table.entityId} = ${table.workspaceId}) OR (${table.entityType} = 'project' AND ${table.workspaceId} IS NULL AND ${table.resourceType} = 'file')))` + ), + entityIdIdx: index('folder_entity_id_idx') + .on(table.entityType, table.entityId, table.id) + .concurrently(), + entityResourceParentNameActiveUnique: uniqueIndex( + 'folder_entity_resource_parent_name_active_unique' + ) + .on( + table.entityType, + table.entityId, + table.resourceType, + sql`coalesce(${table.parentId}, '')`, + table.name + ) + .concurrently() + .where(sql`${table.deletedAt} IS NULL`), userIdx: index('folder_user_idx').on(table.userId), workspaceResourceParentIdx: index('folder_workspace_resource_parent_idx').on( table.workspaceId, @@ -2477,10 +2503,15 @@ export const workspaceFiles = pgTable( 'workspace_files', { id: text('id').primaryKey(), + /** Canonical ownership, independent of purpose, creator, and secret source. NULL awaits repair. */ + // contract-pending(after entity-aware readers and audited backfill are deployed): require both ownership fields. + entityType: text('entity_type'), + entityId: text('entity_id'), key: text('key').notNull(), - userId: text('user_id') - .notNull() - .references(() => user.id, { onDelete: 'cascade' }), + /** Project creator references clear on account deletion; other file contexts retain their cascade. */ + userId: text('user_id').references(() => user.id, { onDelete: 'cascade' }), + /** Immutable original Project creator, independent of the current lifecycle owner and payer. */ + originalCreatorUserId: text('original_creator_user_id'), workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'cascade' }), organizationId: text('organization_id').references(() => organization.id, { onDelete: 'cascade', @@ -2558,6 +2589,28 @@ export const workspaceFiles = pgTable( secretProvenanceVersion: integer('secret_provenance_version'), }, (table) => ({ + creatorLifetimeCheck: check( + 'workspace_files_creator_lifetime_check', + sql`${table.userId} IS NOT NULL OR coalesce(${table.entityType} = 'project' AND char_length(${table.originalCreatorUserId}) > 0, false)` + ), + entityBindingCheck: check( + 'workspace_files_entity_binding_check', + sql`(${table.entityType} IS NULL AND ${table.entityId} IS NULL) OR (${table.entityType} IS NOT NULL AND ${table.entityId} IS NOT NULL AND ${table.entityType} IN ('workspace', 'project', 'organization', 'user') AND char_length(${table.entityId}) > 0)` + ), + entityIdIdx: index('workspace_files_entity_id_idx') + .on(table.entityType, table.entityId, table.id) + .concurrently(), + entityFolderOriginalNameActiveUnique: uniqueIndex( + 'workspace_files_entity_folder_name_active_unique' + ) + .on( + table.entityType, + table.entityId, + sql`coalesce(${table.folderId}, '')`, + table.originalName + ) + .concurrently() + .where(sql`${table.deletedAt} IS NULL AND ${table.context} IN ('workspace', 'project')`), keyActiveUniqueIdx: uniqueIndex('workspace_files_key_active_unique') .on(table.key) .where(sql`${table.deletedAt} IS NULL`), @@ -2580,6 +2633,16 @@ export const workspaceFiles = pgTable( * The walk must also compare its cursor row-wise (`(workspace_id, id) > (:ws, :id)`) to seek * with this index; `a > x OR (a = x AND b > y)` is only ever a filter. See `seedBackfillPage`. */ + ownerSearchKeysetIdx: index('workspace_files_search_owner_keyset_idx') + .on( + sql`coalesce(${table.entityType}, 'workspace')`, + sql`coalesce(${table.entityId}, ${table.workspaceId})`, + table.id + ) + .where( + sql`${table.deletedAt} IS NULL AND ((${table.context} = 'workspace' AND ${table.workspaceId} IS NOT NULL) OR (${table.context} = 'project' AND ${table.entityType} = 'project' AND ${table.entityId} IS NOT NULL))` + ) + .concurrently(), workspaceActiveKeysetIdx: index('workspace_files_workspace_active_keyset_idx') .on(table.workspaceId, table.id) .concurrently() @@ -2669,7 +2732,8 @@ export const workspaceFileSearchIndex = pgTable( }) ) -/** One bounded scheduler row per workspace with current file revisions awaiting dispatch. */ +// contract-pending(after owner-aware search workers are fully deployed): retire this queue and dual enqueue after old dispatchers and retries drain. +/** @deprecated Compatibility scheduling for previously deployed workspace workers. */ export const workspaceFileSearchDispatchQueue = pgTable( 'workspace_file_search_dispatch_queue', { @@ -2692,10 +2756,37 @@ export const workspaceFileSearchDispatchQueue = pgTable( }) ) +/** Owner-neutral dispatch replaces the workspace-only queue after older workers drain. */ +export const fileSearchDispatchQueue = pgTable( + 'file_search_dispatch_queue', + { + entityType: text('entity_type').notNull(), + entityId: text('entity_id').notNull(), + enqueuedAt: timestamp('enqueued_at').notNull().defaultNow(), + lastDispatchedAt: timestamp('last_dispatched_at'), + updatedAt: timestamp('updated_at').notNull().defaultNow(), + }, + (table) => ({ + pk: primaryKey({ columns: [table.entityType, table.entityId] }), + ownerCheck: check( + 'file_search_dispatch_owner_check', + sql`${table.entityType} IN ('workspace', 'project') AND length(${table.entityId}) > 0` + ), + scheduleIdx: index('file_search_dispatch_schedule_idx').on( + table.lastDispatchedAt.asc().nullsFirst(), + table.enqueuedAt, + table.entityType, + table.entityId + ), + }) +) + /** Singleton keyset cursor for the resumable initial workspace-file search backfill. */ export const workspaceFileSearchBackfill = pgTable('workspace_file_search_backfill', { id: text('id').primaryKey(), afterWorkspaceId: text('after_workspace_id'), + afterEntityType: text('after_entity_type'), + afterEntityId: text('after_entity_id'), afterFileId: text('after_file_id'), completedAt: timestamp('completed_at'), updatedAt: timestamp('updated_at').notNull().defaultNow(), @@ -2748,13 +2839,20 @@ export const workspaceFileSearchBuild = pgTable( { id: text('id').primaryKey(), fileId: text('file_id').notNull(), - workspaceId: text('workspace_id').notNull(), + workspaceId: text('workspace_id'), + entityType: text('entity_type'), + entityId: text('entity_id'), + artifactKey: text('artifact_key'), sourceContentUpdatedAt: timestamp('source_content_updated_at').notNull(), createdAt: timestamp('created_at').notNull().defaultNow(), /** Null only for a completely published build; abandoned workers expire automatically. */ expiresAt: timestamp('expires_at'), }, (table) => ({ + ownerCheck: check( + 'file_search_build_owner_check', + sql`(${table.entityType} IS NULL AND ${table.entityId} IS NULL AND ${table.workspaceId} IS NOT NULL) OR (${table.entityType} = 'workspace' AND ${table.entityId} = ${table.workspaceId}) OR (${table.entityType} = 'project' AND length(${table.entityId}) > 0 AND ${table.workspaceId} IS NULL)` + ), fileIdx: index('workspace_file_search_build_file_idx').on(table.fileId), cleanupIdx: index('workspace_file_search_build_cleanup_idx') .on(table.expiresAt, table.id) @@ -2769,7 +2867,9 @@ export const workspaceFileSearchRevision = pgTable( fileId: text('file_id') .primaryKey() .references(() => workspaceFiles.id, { onDelete: 'cascade' }), - workspaceId: text('workspace_id').notNull(), + workspaceId: text('workspace_id'), + entityType: text('entity_type'), + entityId: text('entity_id'), sourceContentUpdatedAt: timestamp('source_content_updated_at').notNull(), status: workspaceFileSearchIndexStatusEnum('status').notNull().default('pending'), buildId: text('build_id').references(() => workspaceFileSearchBuild.id, { @@ -2791,6 +2891,28 @@ export const workspaceFileSearchRevision = pgTable( updatedAt: timestamp('updated_at').notNull().defaultNow(), }, (table) => ({ + ownerCheck: check( + 'file_search_revision_owner_check', + sql`(${table.entityType} IS NULL AND ${table.entityId} IS NULL AND ${table.workspaceId} IS NOT NULL) OR (${table.entityType} = 'workspace' AND ${table.entityId} = ${table.workspaceId}) OR (${table.entityType} = 'project' AND length(${table.entityId}) > 0 AND ${table.workspaceId} IS NULL)` + ), + ownerPendingIdx: index('file_search_revision_owner_pending_idx') + .on( + sql`coalesce(${table.entityType}, 'workspace')`, + sql`coalesce(${table.entityId}, ${table.workspaceId})`, + table.updatedAt, + table.fileId, + table.sourceContentUpdatedAt + ) + .where(sql`${table.status} = 'pending' AND ${table.dispatchedAt} IS NULL`) + .concurrently(), + ownerStatusIdx: index('file_search_revision_owner_status_idx') + .on( + sql`coalesce(${table.entityType}, 'workspace')`, + sql`coalesce(${table.entityId}, ${table.workspaceId})`, + table.status, + table.dispatchedAt + ) + .concurrently(), workspaceStatusIdx: index('workspace_file_search_revision_workspace_status_idx').on( table.workspaceId, table.status @@ -2812,7 +2934,9 @@ export const workspaceFileSearchChunk = pgTable( buildId: text('build_id') .notNull() .references(() => workspaceFileSearchBuild.id), - workspaceId: text('workspace_id').notNull(), + workspaceId: text('workspace_id'), + entityType: text('entity_type'), + entityId: text('entity_id'), ordinal: integer('ordinal').notNull(), lineStart: integer('line_start').notNull(), fragment: boolean('fragment').notNull(), @@ -2820,6 +2944,20 @@ export const workspaceFileSearchChunk = pgTable( content: text('content').notNull(), }, (table) => ({ + ownerCheck: check( + 'file_search_chunk_owner_check', + sql`(${table.entityType} IS NULL AND ${table.entityId} IS NULL AND ${table.workspaceId} IS NOT NULL) OR (${table.entityType} = 'workspace' AND ${table.entityId} = ${table.workspaceId}) OR (${table.entityType} = 'project' AND length(${table.entityId}) > 0 AND ${table.workspaceId} IS NULL)` + ), + ownerContentIdx: index('file_search_chunk_owner_content_idx') + .using( + 'gin', + table.entityType.asc().op('text_ops'), + table.entityId.asc().op('text_ops'), + table.content.asc().op('gin_trgm_ops') + ) + .where(sql`${table.entityType} = 'project'`) + .with({ fastupdate: 'off' }) + .concurrently(), pk: primaryKey({ name: 'workspace_file_search_chunk_pk', columns: [table.buildId, table.ordinal], @@ -2845,6 +2983,23 @@ export const workspaceFileSearchChunk = pgTable( }) ) +/** A bounded compiled-input manifest outlives input deletion until its build is retired. */ +export const fileSearchDependency = pgTable( + 'file_search_dependency', + { + buildId: text('build_id') + .notNull() + .references(() => workspaceFileSearchBuild.id, { onDelete: 'cascade' }), + fileId: text('file_id').notNull(), + key: text('key').notNull(), + sourceContentUpdatedAt: timestamp('source_content_updated_at').notNull(), + }, + (table) => ({ + pk: primaryKey({ columns: [table.buildId, table.fileId] }), + fileIdx: index('file_search_dependency_file_idx').on(table.fileId, table.buildId), + }) +) + export const uploadSessionStatusEnum = pgEnum('upload_session_status', [ 'uploading', 'completing', @@ -2867,6 +3022,7 @@ export const uploadSessionProviderEnum = pgEnum('upload_session_provider', [ export const uploadSessionPurposeEnum = pgEnum('upload_session_purpose', [ 'workspace_file', + 'project_file', 'table_import', 'knowledge_document', 'profile_picture', @@ -3025,9 +3181,8 @@ export const workspaceFileVersion = pgTable( fileId: text('file_id') .notNull() .references(() => workspaceFiles.id, { onDelete: 'cascade' }), - workspaceId: text('workspace_id') - .notNull() - .references(() => workspace.id, { onDelete: 'cascade' }), + /** Compatibility scope, checked against fileId; Project versions derive their owner from the file. */ + workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'cascade' }), version: integer('version').notNull(), key: text('key').notNull(), sizeBytes: bigint('size_bytes', { mode: 'number' }).notNull(), @@ -3082,12 +3237,12 @@ export const publicShare = pgTable( /** 'file' | 'folder' (folder reserved for future) */ resourceType: text('resource_type').notNull(), resourceId: text('resource_id').notNull(), - workspaceId: text('workspace_id') - .notNull() - .references(() => workspace.id, { onDelete: 'cascade' }), + entityType: text('entity_type'), + entityId: text('entity_id'), + workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'cascade' }), /** * SET NULL (not CASCADE) so a share — and its public link — outlives the user who created it; - * the file still belongs to the workspace. + * the shared file keeps its durable owner. */ createdBy: text('created_by').references(() => user.id, { onDelete: 'set null' }), token: text('token').notNull(), @@ -3102,6 +3257,20 @@ export const publicShare = pgTable( updatedAt: timestamp('updated_at').notNull().defaultNow(), }, (table) => ({ + entityBinding: check( + 'public_share_entity_binding_check', + sql` + (${table.entityType} IS NULL AND ${table.entityId} IS NULL AND ${table.workspaceId} IS NOT NULL) + OR (${table.entityType} IS NOT NULL AND ${table.entityId} IS NOT NULL AND char_length(${table.entityId}) > 0 + AND ((${table.entityType} = 'workspace' AND ${table.workspaceId} IS NOT NULL AND ${table.entityId} = ${table.workspaceId}) + OR (${table.entityType} = 'project' AND ${table.workspaceId} IS NULL AND ${table.resourceType} = 'file'))) + ` + ), + entityIdIdx: index('public_share_entity_id_idx').on( + table.entityType, + table.entityId, + table.resourceType + ), tokenIdx: uniqueIndex('public_share_token_unique').on(table.token), resourceUniqueIdx: uniqueIndex('public_share_resource_unique').on( table.resourceType, diff --git a/packages/db/script-migrations/0003_backfill_workspace_storage_usage.ts b/packages/db/script-migrations/0003_backfill_workspace_storage_usage.ts index 162035e6e68..a0e8128aceb 100644 --- a/packages/db/script-migrations/0003_backfill_workspace_storage_usage.ts +++ b/packages/db/script-migrations/0003_backfill_workspace_storage_usage.ts @@ -23,6 +23,16 @@ interface WorkspaceStorageReconciliationOptions { reconcilePayers?: boolean } +function canonicalPayerBytes( + total: { storage_used_bytes: number | string; invalid_count: number | string } | undefined +): number { + const bytes = Number(total?.storage_used_bytes ?? 0) + if (Number(total?.invalid_count ?? 0) > 0 || !Number.isSafeInteger(bytes) || bytes < 0) { + throw new Error('Cannot reconcile payer storage: invalid canonical size metadata') + } + return bytes +} + async function processKeysetPages( listIds: (afterId: string, limit: number) => Promise, processIds: (ids: string[]) => Promise, @@ -64,7 +74,8 @@ async function processPayers( * * Workspace bytes include every durable `workspace_files` row in the * `workspace` context, including archived rows, plus active non-connector - * knowledge documents. Mothership files and connector documents are excluded. + * knowledge documents. Payer totals also include each Project's retained current + * file heads exactly once. Mothership files, versions, and connector documents are excluded. */ export async function reconcileWorkspaceStorageAccounting( store: StorageReconciliationStore, @@ -119,7 +130,7 @@ export function createPostgresStorageReconciliationStore(sql: Sql): StorageRecon FROM workspace WHERE id = ANY(${workspaceIds}::text[]) ORDER BY id - FOR UPDATE + FOR NO KEY UPDATE ` const [invalid] = await tx>` @@ -199,18 +210,31 @@ export function createPostgresStorageReconciliationStore(sql: Sql): StorageRecon SELECT id FROM organization WHERE id = ${organizationId} - FOR UPDATE + FOR NO KEY UPDATE ` if (locked.length === 0) return - const [total] = await tx>` - SELECT coalesce(sum(storage_used_bytes), 0)::bigint AS storage_used_bytes - FROM workspace - WHERE organization_id = ${organizationId} + const [total] = await tx< + Array<{ + storage_used_bytes: number | string + invalid_count: number | string + }> + >` + SELECT coalesce(sum(bytes), 0)::bigint AS storage_used_bytes, + count(*) FILTER (WHERE bytes IS NULL OR bytes < 0) AS invalid_count + FROM ( + SELECT storage_used_bytes AS bytes FROM workspace + WHERE organization_id = ${organizationId} + UNION ALL + SELECT file.size_bytes AS bytes FROM workspace_files file + INNER JOIN project ON project.id = file.entity_id + WHERE file.entity_type = 'project' AND file.context = 'project' + AND project.organization_id = ${organizationId} + ) retained ` await tx` UPDATE organization - SET storage_used_bytes = ${total?.storage_used_bytes ?? 0} + SET storage_used_bytes = ${canonicalPayerBytes(total)} WHERE id = ${organizationId} ` }) @@ -233,19 +257,31 @@ export function createPostgresStorageReconciliationStore(sql: Sql): StorageRecon SELECT user_id AS id FROM user_stats WHERE user_id = ${userId} - FOR UPDATE + FOR NO KEY UPDATE ` if (locked.length === 0) return - const [total] = await tx>` - SELECT coalesce(sum(storage_used_bytes), 0)::bigint AS storage_used_bytes - FROM workspace - WHERE organization_id IS NULL - AND billed_account_user_id = ${userId} + const [total] = await tx< + Array<{ + storage_used_bytes: number | string + invalid_count: number | string + }> + >` + SELECT coalesce(sum(bytes), 0)::bigint AS storage_used_bytes, + count(*) FILTER (WHERE bytes IS NULL OR bytes < 0) AS invalid_count + FROM ( + SELECT storage_used_bytes AS bytes FROM workspace + WHERE organization_id IS NULL AND billed_account_user_id = ${userId} + UNION ALL + SELECT file.size_bytes AS bytes FROM workspace_files file + INNER JOIN project ON project.id = file.entity_id + WHERE file.entity_type = 'project' AND file.context = 'project' + AND project.organization_id IS NULL AND project.owner_id = ${userId} + ) retained ` await tx` UPDATE user_stats - SET storage_used_bytes = ${total?.storage_used_bytes ?? 0} + SET storage_used_bytes = ${canonicalPayerBytes(total)} WHERE user_id = ${userId} ` }) diff --git a/packages/db/script-migrations/0030_backfill_workspace_file_entities.ts b/packages/db/script-migrations/0030_backfill_workspace_file_entities.ts new file mode 100644 index 00000000000..84c7d6b6ada --- /dev/null +++ b/packages/db/script-migrations/0030_backfill_workspace_file_entities.ts @@ -0,0 +1,88 @@ +import type { ScriptMigration } from '@sim/db/script-migrations/types' +import { createLogger } from '@sim/logger' +import type { Sql } from 'postgres' + +const logger = createLogger('FileEntityBackfill') +const BATCH_SIZE = 500 +const MAX_REPAIR_SAMPLES = 10 + +interface UnresolvedContext { + context: string + count: number + sampleFileIds: string[] +} + +/** Populates only audited legacy owners without changing file identities, bytes, or revisions. */ +export async function backfillWorkspaceFileEntities(sql: Sql) { + let afterFileId = '' + let scanned = 0 + let backfilled = 0 + const unresolved = new Map() + + for (;;) { + const rows = await sql< + { id: string; context: string; mappedType: string | null; mappedId: string | null }[] + >` + SELECT file.id, file.context, owner.entity_type AS "mappedType", owner.entity_id AS "mappedId" + FROM workspace_files file + CROSS JOIN LATERAL workspace_file_legacy_entity( + file.context, file.workspace_id, file.organization_id, file.user_id + ) owner + WHERE file.entity_type IS NULL AND file.entity_id IS NULL AND file.id > ${afterFileId} + ORDER BY file.id + LIMIT ${BATCH_SIZE} + ` + if (!rows.length) break + afterFileId = rows[rows.length - 1].id + scanned += rows.length + + for (const row of rows) { + if (row.mappedType && row.mappedId) continue + let summary = unresolved.get(row.context) + if (!summary) { + summary = { context: row.context, count: 0, sampleFileIds: [] } + unresolved.set(row.context, summary) + } + summary.count += 1 + if (summary.sampleFileIds.length < MAX_REPAIR_SAMPLES) summary.sampleFileIds.push(row.id) + } + + const ids = rows.filter((row) => row.mappedType && row.mappedId).map((row) => row.id) + if (!ids.length) continue + const updated = await sql.begin(async (tx) => { + await tx`SET LOCAL lock_timeout = '5s'` + await tx`SET LOCAL statement_timeout = '30s'` + /** Lock in key order; the trigger derives from the current row after concurrent legacy writes. */ + await tx` + SELECT id FROM workspace_files WHERE id = ANY(${ids}::text[]) ORDER BY id FOR UPDATE + ` + return tx<{ id: string }[]>` + UPDATE workspace_files AS file SET entity_type = file.entity_type + WHERE file.id = ANY(${ids}::text[]) + AND file.entity_type IS NULL AND file.entity_id IS NULL + AND EXISTS ( + SELECT 1 FROM workspace_file_legacy_entity( + file.context, file.workspace_id, file.organization_id, file.user_id + ) owner WHERE owner.entity_type IS NOT NULL AND owner.entity_id IS NOT NULL + ) + RETURNING file.id + ` + }) + backfilled += updated.length + } + + return { scanned, backfilled, unresolved: [...unresolved.values()] } +} + +export const backfillWorkspaceFileEntitiesMigration: ScriptMigration = { + name: '0030_backfill_workspace_file_entities', + async up(sql) { + const result = await backfillWorkspaceFileEntities(sql) + logger.info('File entity ownership backfill completed', result) + if (result.unresolved.length) { + logger.warn('Unresolved legacy file owners remain unavailable to entity-scoped operations', { + unresolved: result.unresolved, + }) + } + }, +} diff --git a/packages/db/script-migrations/0031_backfill_file_folder_entities.ts b/packages/db/script-migrations/0031_backfill_file_folder_entities.ts new file mode 100644 index 00000000000..b059df7b81e --- /dev/null +++ b/packages/db/script-migrations/0031_backfill_file_folder_entities.ts @@ -0,0 +1,80 @@ +import type { ScriptMigration } from '@sim/db/script-migrations/types' +import { createLogger } from '@sim/logger' +import type { Sql } from 'postgres' + +const logger = createLogger('FileFolderEntityBackfill') +const BATCH_SIZE = 500 +const MAX_REPAIR_SAMPLES = 10 + +/** Backfills compatibility folder ownership; mismatched version scopes are reported without reassignment. */ +export async function backfillFileFolderEntities(sql: Sql) { + let afterFolderId = '' + let backfilled = 0 + for (;;) { + const rows = await sql<{ id: string }[]>` + SELECT id FROM folder + WHERE entity_type IS NULL AND entity_id IS NULL AND workspace_id IS NOT NULL + AND id > ${afterFolderId} + ORDER BY id LIMIT ${BATCH_SIZE} + ` + if (!rows.length) break + afterFolderId = rows[rows.length - 1].id + const ids = rows.map((row) => row.id) + const updated = await sql.begin(async (tx) => { + await tx`SET LOCAL lock_timeout = '5s'` + await tx`SET LOCAL statement_timeout = '30s'` + await tx`SELECT id FROM folder WHERE id = ANY(${ids}::text[]) ORDER BY id FOR UPDATE` + return tx<{ id: string }[]>` + UPDATE folder SET entity_type = entity_type + WHERE id = ANY(${ids}::text[]) AND entity_type IS NULL AND entity_id IS NULL + AND workspace_id IS NOT NULL + RETURNING id + ` + }) + backfilled += updated.length + } + + let afterVersionId = '' + let mismatchedVersions = 0 + const sampleVersionIds: string[] = [] + for (;;) { + const rows = await sql<{ id: string; compatible: boolean }[]>` + SELECT version.id, + coalesce( + (coalesce(file.entity_type, owner.entity_type) = 'workspace' AND file.context = 'workspace' + AND version.workspace_id IS NOT DISTINCT FROM coalesce(file.entity_id, owner.entity_id)) + OR (file.entity_type = 'project' AND file.context = 'project' AND version.workspace_id IS NULL), + false + ) AS compatible + FROM workspace_file_version version + LEFT JOIN workspace_files file ON file.id = version.file_id + LEFT JOIN LATERAL workspace_file_legacy_entity( + file.context, file.workspace_id, file.organization_id, file.user_id + ) owner ON true + WHERE version.id > ${afterVersionId} + ORDER BY version.id LIMIT ${BATCH_SIZE} + ` + if (!rows.length) break + afterVersionId = rows[rows.length - 1].id + for (const row of rows) { + if (row.compatible) continue + mismatchedVersions += 1 + if (sampleVersionIds.length < MAX_REPAIR_SAMPLES) sampleVersionIds.push(row.id) + } + } + + return { backfilled, mismatchedVersions, sampleVersionIds } +} + +export const backfillFileFolderEntitiesMigration: ScriptMigration = { + name: '0031_backfill_file_folder_entities', + async up(sql) { + const result = await backfillFileFolderEntities(sql) + logger.info('File folder entity backfill completed', result) + if (result.mismatchedVersions) { + throw new Error( + 'Retained file versions require ownership repair before continuing. Review the reported version IDs, repair their ownership explicitly, and rerun the migration.' + ) + } + }, +} diff --git a/packages/db/script-migrations/0032_backfill_project_file_creators.ts b/packages/db/script-migrations/0032_backfill_project_file_creators.ts new file mode 100644 index 00000000000..d39ed6a18bd --- /dev/null +++ b/packages/db/script-migrations/0032_backfill_project_file_creators.ts @@ -0,0 +1,49 @@ +import type { ScriptMigration } from '@sim/db/script-migrations/types' +import { createLogger } from '@sim/logger' +import type { Sql } from 'postgres' + +const logger = createLogger('ProjectFileCreatorBackfill') +const BATCH_SIZE = 500 + +async function backfillTable(sql: Sql, table: 'workspace_files' | 'folder'): Promise { + let afterId = '' + let backfilled = 0 + for (;;) { + const rows = await sql<{ id: string }[]>` + SELECT id FROM ${sql(table)} + WHERE entity_type = 'project' AND original_creator_user_id IS NULL AND user_id IS NOT NULL + AND id > ${afterId} + ORDER BY id LIMIT ${BATCH_SIZE} + ` + if (!rows.length) return backfilled + afterId = rows[rows.length - 1].id + const ids = rows.map((row) => row.id) + const updated = await sql.begin(async (tx) => { + await tx`SET LOCAL lock_timeout = '5s'` + await tx`SET LOCAL statement_timeout = '30s'` + await tx`SELECT id FROM ${tx(table)} WHERE id = ANY(${ids}::text[]) ORDER BY id FOR UPDATE` + return tx<{ id: string }[]>` + UPDATE ${tx(table)} SET original_creator_user_id = user_id + WHERE id = ANY(${ids}::text[]) AND entity_type = 'project' + AND original_creator_user_id IS NULL AND user_id IS NOT NULL + RETURNING id + ` + }) + backfilled += updated.length + } +} + +/** Snapshots canonical Project creators without changing live references, ownership, or content. */ +export async function backfillProjectFileCreators(sql: Sql) { + return { + files: await backfillTable(sql, 'workspace_files'), + folders: await backfillTable(sql, 'folder'), + } +} + +export const backfillProjectFileCreatorsMigration: ScriptMigration = { + name: '0032_backfill_project_file_creators', + async up(sql) { + logger.info('Project file creator backfill completed', await backfillProjectFileCreators(sql)) + }, +} diff --git a/packages/db/script-migrations/0033_backfill_public_share_entities.ts b/packages/db/script-migrations/0033_backfill_public_share_entities.ts new file mode 100644 index 00000000000..c3453c76ccb --- /dev/null +++ b/packages/db/script-migrations/0033_backfill_public_share_entities.ts @@ -0,0 +1,82 @@ +import type { ScriptMigration } from '@sim/db/script-migrations/types' +import { createLogger } from '@sim/logger' +import type { Sql } from 'postgres' + +const logger = createLogger('PublicShareEntityBackfill') +const BATCH_SIZE = 500 +const MAX_REPAIR_SAMPLES = 10 + +/** Stamps verified legacy share owners without repairing ambiguous targets or changing tokens. */ +export async function backfillPublicShareEntities(sql: Sql) { + let afterId = '' + let backfilled = 0 + for (;;) { + const rows = await sql<{ id: string; resource_type: string; resource_id: string }[]>` + SELECT id, resource_type, resource_id FROM public_share + WHERE entity_type IS NULL AND entity_id IS NULL AND workspace_id IS NOT NULL + AND id > ${afterId} + ORDER BY id LIMIT ${BATCH_SIZE} + ` + if (!rows.length) break + afterId = rows[rows.length - 1].id + const ids = rows.map((row) => row.id) + const fileIds = [ + ...new Set(rows.filter((row) => row.resource_type === 'file').map((row) => row.resource_id)), + ].sort() + const updated = await sql.begin(async (tx) => { + await tx`SET LOCAL lock_timeout = '5s'` + await tx`SET LOCAL statement_timeout = '30s'` + // Target locks precede share locks, as they do for insertion and target retirement. + if (fileIds.length) + await tx` + SELECT id FROM workspace_files WHERE id = ANY(${fileIds}::text[]) + ORDER BY id FOR UPDATE + ` + return tx<{ id: string }[]>` + UPDATE public_share share + SET entity_type = 'workspace', entity_id = share.workspace_id + WHERE share.id = ANY(${ids}::text[]) AND share.entity_type IS NULL AND share.entity_id IS NULL + AND share.workspace_id IS NOT NULL + AND (share.resource_type <> 'file' OR EXISTS ( + SELECT 1 FROM workspace_files file + CROSS JOIN LATERAL workspace_file_legacy_entity( + file.context, file.workspace_id, file.organization_id, file.user_id + ) owner + WHERE file.id = share.resource_id + AND coalesce(file.entity_type, owner.entity_type) = 'workspace' + AND coalesce(file.entity_id, owner.entity_id) = share.workspace_id + )) + RETURNING share.id + ` + }) + backfilled += updated.length + } + + let unresolved = 0 + const sampleShareIds: string[] = [] + afterId = '' + for (;;) { + const rows = await sql<{ id: string }[]>` + SELECT id FROM public_share + WHERE (entity_type IS NULL OR entity_id IS NULL) AND id > ${afterId} + ORDER BY id LIMIT ${BATCH_SIZE} + ` + if (!rows.length) break + afterId = rows[rows.length - 1].id + unresolved += rows.length + for (const row of rows) { + if (sampleShareIds.length < MAX_REPAIR_SAMPLES) sampleShareIds.push(row.id) + } + } + return { backfilled, unresolved, sampleShareIds } +} + +export const backfillPublicShareEntitiesMigration: ScriptMigration = { + name: '0033_backfill_public_share_entities', + async up(sql) { + const result = await backfillPublicShareEntities(sql) + logger.info('Public share owner backfill completed', result) + if (result.unresolved) + logger.warn('Public shares require target repair before delivery', result) + }, +} diff --git a/packages/db/script-migrations/index.ts b/packages/db/script-migrations/index.ts index 084c707c333..a75564763f3 100644 --- a/packages/db/script-migrations/index.ts +++ b/packages/db/script-migrations/index.ts @@ -10,6 +10,10 @@ import { projectionAclSkipUnfilledMigration } from '@sim/db/script-migrations/00 import { knowledgeProjectionAsyncMigration } from '@sim/db/script-migrations/0024_knowledge_projection_async' import { scopeKeywordProjectionsMigration } from '@sim/db/script-migrations/0025_scope_keyword_projections' import { userTableSchemaForWriteMigration } from '@sim/db/script-migrations/0026_user_table_schema_for_write' +import { backfillWorkspaceFileEntitiesMigration } from '@sim/db/script-migrations/0030_backfill_workspace_file_entities' +import { backfillFileFolderEntitiesMigration } from '@sim/db/script-migrations/0031_backfill_file_folder_entities' +import { backfillProjectFileCreatorsMigration } from '@sim/db/script-migrations/0032_backfill_project_file_creators' +import { backfillPublicShareEntitiesMigration } from '@sim/db/script-migrations/0033_backfill_public_share_entities' import type { Sql } from 'postgres' import { backfillTableOrderKeys } from './0001_backfill_table_order_keys' import { backfillPausedBillingAttribution } from './0002_backfill_paused_billing_attribution' @@ -62,6 +66,10 @@ export const scriptMigrations: readonly ScriptMigration[] = [ * Search retirement (0027–0029) is an operator-run maintenance command, not a deploy step: * run `packages/db/scripts/retire-indexed-search.ts --help` for usage. */ + backfillWorkspaceFileEntitiesMigration, + backfillFileFolderEntitiesMigration, + backfillProjectFileCreatorsMigration, + backfillPublicShareEntitiesMigration, ] /** diff --git a/packages/platform-authz/src/room-policy.ts b/packages/platform-authz/src/room-policy.ts index 73ea28105ed..ba91920c852 100644 --- a/packages/platform-authz/src/room-policy.ts +++ b/packages/platform-authz/src/room-policy.ts @@ -24,6 +24,7 @@ export const ROOM_MEMBERSHIP_ACTIONS = { [ROOM_TYPES.WORKSPACE_TABLES]: 'read', [ROOM_TYPES.WORKSPACE_WORKFLOWS]: 'read', [ROOM_TYPES.WORKSPACE_FILE_DOC]: 'write', + [ROOM_TYPES.PROJECT_FILE_DOC]: 'read', [ROOM_TYPES.TABLE]: 'read', } as const satisfies Record diff --git a/packages/realtime-protocol/src/file-doc.ts b/packages/realtime-protocol/src/file-doc.ts index 18e22e2e81c..f5243104889 100644 --- a/packages/realtime-protocol/src/file-doc.ts +++ b/packages/realtime-protocol/src/file-doc.ts @@ -21,6 +21,7 @@ export const FILE_DOC_EVENTS = { JOIN_SUCCESS: 'join-file-doc-success', /** Server → client: join rejected ({@link JoinFileDocError}). */ JOIN_ERROR: 'join-file-doc-error', + PERMISSION: 'file-doc-permission', /** Client → server: leave the session ({@link LeaveFileDocPayload}). */ LEAVE: 'leave-file-doc', /** Both directions: a framed Yjs message (binary), tagged by {@link FILE_DOC_MESSAGE_TYPE}. */ @@ -147,6 +148,7 @@ export const FILE_DOC_LIMITS = { /** Client → server join request. `fileId` is the `workspace_files.id`. */ export interface JoinFileDocPayload { + projectId?: string fileId: string /** * The joining Yjs document's `clientID`. The server binds it to this socket so @@ -160,6 +162,8 @@ export interface JoinFileDocPayload { /** Server → client acceptance of a {@link FILE_DOC_EVENTS.JOIN}. */ export interface JoinFileDocSuccess { + canWrite?: boolean + projectId?: string fileId: string /** The provider whose join was accepted. Optional while older relays are still deployed. */ clientId?: number @@ -190,6 +194,7 @@ export interface JoinFileDocError { /** Client → server leave request. */ export interface LeaveFileDocPayload { + projectId?: string fileId: string } @@ -205,6 +210,7 @@ export interface FileDocInvalidated { /** A bounded, retry-safe batch of user-authored changes. */ export interface FileDocUpdatePayload { + projectId?: string fileId: string docId: string updateId: string @@ -255,3 +261,16 @@ export function toFileDocBytes(data: unknown): Uint8Array | null { if (data instanceof ArrayBuffer) return new Uint8Array(data) return null } + +/** Authenticated relay identity assertions, accepted only with the internal service credential. */ +export const FILE_DOC_INTERNAL_HEADERS = { + userId: 'x-sim-subject-user-id', + connectionId: 'x-sim-realtime-connection-id', +} as const + +/** Current write capability for a subscribed Project document. */ +export interface FileDocPermission { + projectId: string + fileId: string + canWrite: boolean +} diff --git a/packages/realtime-protocol/src/rooms.ts b/packages/realtime-protocol/src/rooms.ts index c1751ba1a04..2de4b1128ea 100644 --- a/packages/realtime-protocol/src/rooms.ts +++ b/packages/realtime-protocol/src/rooms.ts @@ -29,6 +29,7 @@ export const ROOM_TYPES = { * workspace-scoped {@link ROOM_TYPES.WORKSPACE_FILES} browser room. */ WORKSPACE_FILE_DOC: 'workspace-file-doc', + PROJECT_FILE_DOC: 'project-file-doc', /** * A single table's grid (one room per table). Carries live cell-selection * presence — which cells each viewer has selected — so its id space is the @@ -135,3 +136,19 @@ export function parseRoomName(name: string): RoomRef | null { export function presenceEventName(type: RoomType): string { return type === ROOM_TYPES.WORKFLOW ? 'presence-update' : `${type}:presence-update` } + +/** Owner-qualified identity for shared Project document rooms. */ +export function projectFileDocRoom(projectId: string, fileId: string): RoomRef { + if (![projectId, fileId].every((id) => id.length > 0 && !/[/:]/.test(id))) { + throw new Error('Invalid Project document identity') + } + return { type: ROOM_TYPES.PROJECT_FILE_DOC, id: `${projectId}/${fileId}` } +} + +/** Resolve the exact owner and file encoded in a Project room address. */ +export function projectFileDocTarget(room: RoomRef): { projectId: string; fileId: string } | null { + if (room.type !== ROOM_TYPES.PROJECT_FILE_DOC) return null + const parts = room.id.split('/') + if (parts.length !== 2 || parts.some((id) => !id || id.includes(':'))) return null + return { projectId: parts[0], fileId: parts[1] } +} diff --git a/packages/testing/src/mocks/billing-payer-transfer.mock.ts b/packages/testing/src/mocks/billing-payer-transfer.mock.ts new file mode 100644 index 00000000000..6a0c8993a5d --- /dev/null +++ b/packages/testing/src/mocks/billing-payer-transfer.mock.ts @@ -0,0 +1,19 @@ +import { vi } from 'vitest' + +/** Controllable compound billing boundaries for workspace and account lifecycle tests. */ +export const billingPayerTransferMockFns = { + mockChangeOrganizationWorkspaceBilledAccountsInTx: vi.fn(), + mockChangeWorkspaceStoragePayerInTx: vi.fn(), + mockChangeWorkspaceStoragePayersInTx: vi.fn(), + mockChangeProjectAndWorkspaceStoragePayersInTx: vi.fn(), +} + +export const billingPayerTransferMock = { + changeOrganizationWorkspaceBilledAccountsInTx: + billingPayerTransferMockFns.mockChangeOrganizationWorkspaceBilledAccountsInTx, + changeWorkspaceStoragePayerInTx: billingPayerTransferMockFns.mockChangeWorkspaceStoragePayerInTx, + changeWorkspaceStoragePayersInTx: + billingPayerTransferMockFns.mockChangeWorkspaceStoragePayersInTx, + changeProjectAndWorkspaceStoragePayersInTx: + billingPayerTransferMockFns.mockChangeProjectAndWorkspaceStoragePayersInTx, +} diff --git a/packages/testing/src/mocks/project-membership.mock.ts b/packages/testing/src/mocks/project-membership.mock.ts new file mode 100644 index 00000000000..f079aa2942c --- /dev/null +++ b/packages/testing/src/mocks/project-membership.mock.ts @@ -0,0 +1,6 @@ +import { vi } from 'vitest' + +/** Project membership locks for isolated workspace resource tests. */ +export const projectMembershipMock = { + lockWorkspaceProject: vi.fn(async () => null), +} diff --git a/packages/testing/src/mocks/schema-tables.generated.ts b/packages/testing/src/mocks/schema-tables.generated.ts index 108730788fd..bd5533e8454 100644 --- a/packages/testing/src/mocks/schema-tables.generated.ts +++ b/packages/testing/src/mocks/schema-tables.generated.ts @@ -53,9 +53,12 @@ export const GENERATED_SCHEMA_TABLES = { verification: ['id', 'identifier', 'value', 'expiresAt', 'createdAt', 'updatedAt'], folder: [ 'id', + 'entityType', + 'entityId', 'resourceType', 'name', 'userId', + 'originalCreatorUserId', 'workspaceId', 'parentId', 'locked', @@ -601,8 +604,11 @@ export const GENERATED_SCHEMA_TABLES = { ], workspaceFiles: [ 'id', + 'entityType', + 'entityId', 'key', 'userId', + 'originalCreatorUserId', 'workspaceId', 'organizationId', 'folderId', @@ -635,9 +641,18 @@ export const GENERATED_SCHEMA_TABLES = { 'updatedAt', ], workspaceFileSearchDispatchQueue: ['workspaceId', 'enqueuedAt', 'lastDispatchedAt', 'updatedAt'], + fileSearchDispatchQueue: [ + 'entityType', + 'entityId', + 'enqueuedAt', + 'lastDispatchedAt', + 'updatedAt', + ], workspaceFileSearchBackfill: [ 'id', 'afterWorkspaceId', + 'afterEntityType', + 'afterEntityId', 'afterFileId', 'completedAt', 'updatedAt', @@ -656,6 +671,9 @@ export const GENERATED_SCHEMA_TABLES = { 'id', 'fileId', 'workspaceId', + 'entityType', + 'entityId', + 'artifactKey', 'sourceContentUpdatedAt', 'createdAt', 'expiresAt', @@ -663,6 +681,8 @@ export const GENERATED_SCHEMA_TABLES = { workspaceFileSearchRevision: [ 'fileId', 'workspaceId', + 'entityType', + 'entityId', 'sourceContentUpdatedAt', 'status', 'buildId', @@ -677,12 +697,15 @@ export const GENERATED_SCHEMA_TABLES = { workspaceFileSearchChunk: [ 'buildId', 'workspaceId', + 'entityType', + 'entityId', 'ordinal', 'lineStart', 'fragment', 'overlap', 'content', ], + fileSearchDependency: ['buildId', 'fileId', 'key', 'sourceContentUpdatedAt'], uploadSession: [ 'id', 'tokenHash', @@ -738,6 +761,8 @@ export const GENERATED_SCHEMA_TABLES = { 'id', 'resourceType', 'resourceId', + 'entityType', + 'entityId', 'workspaceId', 'createdBy', 'token', @@ -2082,6 +2107,7 @@ export const GENERATED_SCHEMA_ENUMS = { uploadSessionProviderEnum: ['local', 's3', 'blob', 'gcs'], uploadSessionPurposeEnum: [ 'workspace_file', + 'project_file', 'table_import', 'knowledge_document', 'profile_picture', diff --git a/packages/testing/src/mocks/workspace-file-folders.mock.ts b/packages/testing/src/mocks/workspace-file-folders.mock.ts index 811cec9ce5a..a056dac1549 100644 --- a/packages/testing/src/mocks/workspace-file-folders.mock.ts +++ b/packages/testing/src/mocks/workspace-file-folders.mock.ts @@ -72,6 +72,7 @@ export const workspaceFileFoldersMockFns = { mockGetWorkspaceFileFolderPath: vi.fn(), mockFindWorkspaceFileFolderIdByPath: vi.fn(), mockListWorkspaceFileFolders: vi.fn(async (..._args: unknown[]): Promise => []), + mockListFileFolders: vi.fn(async (..._args: unknown[]): Promise => []), mockGetWorkspaceFileFolder: vi.fn(), mockResolveWorkspaceFileFolderTarget: vi.fn( async (..._args: unknown[]): Promise => null @@ -120,6 +121,7 @@ export const workspaceFileFoldersMock = { getWorkspaceFileFolderPath: fns.mockGetWorkspaceFileFolderPath, findWorkspaceFileFolderIdByPath: fns.mockFindWorkspaceFileFolderIdByPath, listWorkspaceFileFolders: fns.mockListWorkspaceFileFolders, + listFileFolders: fns.mockListFileFolders, getWorkspaceFileFolder: fns.mockGetWorkspaceFileFolder, resolveWorkspaceFileFolderTarget: fns.mockResolveWorkspaceFileFolderTarget, assertWorkspaceFileFolderTarget: fns.mockAssertWorkspaceFileFolderTarget, diff --git a/scripts/check-unused-exports.baseline.json b/scripts/check-unused-exports.baseline.json index 16bb8898a49..87b4aafd27f 100644 --- a/scripts/check-unused-exports.baseline.json +++ b/scripts/check-unused-exports.baseline.json @@ -3755,10 +3755,6 @@ "apps/sim/lib/api/contracts/v2/tables.ts#v2WorkflowGroupDataSchema", "apps/sim/lib/api/contracts/v2/tables.ts#v2WorkflowGroupSchema", "apps/sim/lib/api/contracts/v2/tables.ts#v2WorkspaceScopedBodySchema", - "apps/sim/lib/api/contracts/v2/uploads.ts#V2PartUrlsBody", - "apps/sim/lib/api/contracts/v2/uploads.ts#V2PartUrlsData", - "apps/sim/lib/api/contracts/v2/uploads.ts#V2UploadTokenHeaders", - "apps/sim/lib/api/contracts/v2/uploads.ts#V2UploadTransfer", "apps/sim/lib/api/contracts/v2/uploads.ts#v2MultipartUploadTransferSchema", "apps/sim/lib/api/contracts/v2/uploads.ts#v2PutUploadTransferSchema", "apps/sim/lib/api/contracts/v2/uploads.ts#v2UploadPartUrlSchema", @@ -4991,11 +4987,6 @@ "apps/sim/lib/mothership/tools/client/hidden-tools.ts#getHiddenToolNames", "apps/sim/lib/mothership/tools/local-filesystem.ts#hasUserLocalVfsPrefix", "apps/sim/lib/mothership/tools/permissions.ts#copilotWriteDeniedMessage", - "apps/sim/lib/mothership/tools/server/files/doc-compile.ts#DocEngine", - "apps/sim/lib/mothership/tools/server/files/doc-compile.ts#PYTHON_PDF_SOURCE_MIME", - "apps/sim/lib/mothership/tools/server/files/doc-compile.ts#PYTHON_XLSX_SOURCE_MIME", - "apps/sim/lib/mothership/tools/server/files/doc-compile.ts#isSimdocDeckSource", - "apps/sim/lib/mothership/tools/server/files/doc-compile.ts#loadCompiledDocByExt", "apps/sim/lib/mothership/tools/server/files/embedded-image-refs.ts#findUnembeddableImageRefs", "apps/sim/lib/mothership/tools/server/files/workspace-file.ts#validateFlatWorkspaceFileName", "apps/sim/lib/mothership/tools/server/router.ts#ExecuteResponseSuccess", diff --git a/scripts/test-integration.ts b/scripts/test-integration.ts index 1f7a70f81e6..095a7854a1f 100644 --- a/scripts/test-integration.ts +++ b/scripts/test-integration.ts @@ -9,12 +9,12 @@ import { generateId } from '@sim/utils/id' /** * Runs the real-infrastructure test layer (`*.integration.ts`) locally: `bun run test:integration`. * Creates and removes its own Postgres and Redis containers, provisions the schema the way CI does, - * and runs `vitest run --mode integration` in `packages/db` and `apps/sim` with `TEST_DATABASE_URL` + * and runs `vitest run --mode integration` in `packages/db`, `apps/sim`, and `apps/realtime` with `TEST_DATABASE_URL` * and `TEST_REDIS_URL`; it never reads an application DSN. * Set INTEGRATION_DB_PROVISION=migrate to exercise the versioned SQL migration path. * Set KNOWLEDGE_SCALE_TEST=true for the opt-in scale suite; its JSON report is saved in tmpdir. * Optional positional `apps/sim` Vitest filename filters limit a diagnostic run (and skip - * `packages/db`); omit them for full validation. + * `packages/db` and `apps/realtime`); omit them for full validation. */ const logger = createLogger('IntegrationTests') const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') @@ -182,6 +182,10 @@ try { cwd: path.join(root, 'packages/db'), env: environment, }) + run('bun', ['run', 'test', '--mode', 'integration'], { + cwd: path.join(root, 'apps/realtime'), + env: environment, + }) } logger.info( scale From 2613baae9f7c54e5fda6bc5b9bbbcd1ff0ca8b75 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sun, 4 Oct 2026 03:34:44 -0700 Subject: [PATCH 023/113] test: align file ownership fixtures and validation reports --- .claude/rules/sim-testing.md | 5 ++-- .cursor/rules/sim-testing.mdc | 5 ++-- apps/realtime/src/routes/http.test.ts | 1 - apps/realtime/vitest.config.ts | 4 ++- .../lib/mapping/resources.test.ts | 18 ++++++++++-- .../billing/core/billing-attribution.test.ts | 3 -- apps/sim/lib/logs/folder-scope.test.ts | 10 ++++++- .../file-round-trip-provenance.test.ts | 6 ++++ .../workspace-file-manager-page.test.ts | 29 +++++++------------ .../workspace-file-storage-billing.test.ts | 10 +++---- ...kspace-file-storage-cleanup-outbox.test.ts | 6 ---- .../sim/lib/uploads/documents/compile.test.ts | 5 ++-- .../lib/uploads/documents/servable.test.ts | 3 +- .../file-attachments-authorization.test.ts | 1 - .../src/mocks/project-membership.mock.ts | 1 + .../src/mocks/workspaces-utils.mock.ts | 13 +++++++-- 16 files changed, 73 insertions(+), 47 deletions(-) diff --git a/.claude/rules/sim-testing.md b/.claude/rules/sim-testing.md index dbbb381daac..1888778564e 100644 --- a/.claude/rules/sim-testing.md +++ b/.claude/rules/sim-testing.md @@ -47,8 +47,9 @@ contracts, and demonstrated regressions. (`sim_test`); `TEST_REDIS_URL` must be loopback. `packages/db/testing/test-infrastructure.ts` owns those checks. Isolate with a unique schema or generated IDs, and clean up in `afterAll`. - Integration files run one at a time against one shared database. A new `*.integration.ts` is - picked up by CI with no workflow change, and the run writes `test-results/integration.json`, which - CI uploads. Never add a passing suite to the quarantine list in `apps/sim/vitest.config.ts`. + picked up by CI with no workflow change. By default, each workspace writes its generated report + to `/test-results/integration.json`, which CI uploads. Never add a passing suite to the + quarantine list in `apps/sim/vitest.config.ts`. - `bun run test:integration` starts disposable Postgres and Redis containers, provisions the schema, and runs both workspaces; pass filenames to narrow the `apps/sim` run. diff --git a/.cursor/rules/sim-testing.mdc b/.cursor/rules/sim-testing.mdc index e4cc023d9f4..8fd52992368 100644 --- a/.cursor/rules/sim-testing.mdc +++ b/.cursor/rules/sim-testing.mdc @@ -45,8 +45,9 @@ contracts, and demonstrated regressions. (`sim_test`); `TEST_REDIS_URL` must be loopback. `packages/db/testing/test-infrastructure.ts` owns those checks. Isolate with a unique schema or generated IDs, and clean up in `afterAll`. - Integration files run one at a time against one shared database. A new `*.integration.ts` is - picked up by CI with no workflow change, and the run writes `test-results/integration.json`, which - CI uploads. Never add a passing suite to the quarantine list in `apps/sim/vitest.config.ts`. + picked up by CI with no workflow change. By default, each workspace writes its generated report + to `/test-results/integration.json`, which CI uploads. Never add a passing suite to the + quarantine list in `apps/sim/vitest.config.ts`. - `bun run test:integration` starts disposable Postgres and Redis containers, provisions the schema, and runs both workspaces; pass filenames to narrow the `apps/sim` run. diff --git a/apps/realtime/src/routes/http.test.ts b/apps/realtime/src/routes/http.test.ts index 5468358436c..c4a59953c37 100644 --- a/apps/realtime/src/routes/http.test.ts +++ b/apps/realtime/src/routes/http.test.ts @@ -63,7 +63,6 @@ describe('createHttpHandler', () => { await handler(req, res) - expect(mockInvalidateDocument).toHaveBeenCalledWith('file-1', 100) expect(to).toHaveBeenCalledWith(['workspace-file-doc:file-1', 'file-doc-admission:file-1']) expect(emit).toHaveBeenCalledWith( 'file-doc-invalidated', diff --git a/apps/realtime/vitest.config.ts b/apps/realtime/vitest.config.ts index 539b4220783..870fb3eaf0a 100644 --- a/apps/realtime/vitest.config.ts +++ b/apps/realtime/vitest.config.ts @@ -17,7 +17,9 @@ export default defineConfig(({ mode }) => ? { fileParallelism: false, reporters: ['default', 'json'], - outputFile: { json: 'test-results/integration.json' }, + outputFile: { + json: process.env.INTEGRATION_REPORT_PATH ?? 'test-results/integration.json', + }, } : {}), }, diff --git a/apps/sim/ee/workspace-forking/lib/mapping/resources.test.ts b/apps/sim/ee/workspace-forking/lib/mapping/resources.test.ts index 80811a11154..e4c179d715f 100644 --- a/apps/sim/ee/workspace-forking/lib/mapping/resources.test.ts +++ b/apps/sim/ee/workspace-forking/lib/mapping/resources.test.ts @@ -39,8 +39,22 @@ describe('listForkResourceCandidates', () => { { id: 'workspace/WS/notes.md', label: 'notes.md' }, ]) .mockResolvedValueOnce([ - { id: 'folder-reports', name: 'Reports', parentId: null }, - { id: 'folder-q3', name: 'Q3 Results', parentId: 'folder-reports' }, + { + id: 'folder-reports', + name: 'Reports', + parentId: null, + workspaceId: 'ws-1', + entityType: 'workspace', + entityId: 'ws-1', + }, + { + id: 'folder-q3', + name: 'Q3 Results', + parentId: 'folder-reports', + workspaceId: 'ws-1', + entityType: 'workspace', + entityId: 'ws-1', + }, ]) const result = await listForkResourceCandidates(executor, 'ws-1') diff --git a/apps/sim/lib/billing/core/billing-attribution.test.ts b/apps/sim/lib/billing/core/billing-attribution.test.ts index 8e40d3456da..f5252c629d5 100644 --- a/apps/sim/lib/billing/core/billing-attribution.test.ts +++ b/apps/sim/lib/billing/core/billing-attribution.test.ts @@ -112,9 +112,6 @@ describe('resolveBillingAttribution', () => { }, workspaceId: 'workspace-b', }) - expect(mockGetOrganizationSubscription).toHaveBeenCalledWith('org-b', { - onError: 'throw', - }) expect(mockGetHighestPriorityPersonalSubscription).not.toHaveBeenCalled() expect(Object.isFrozen(attribution)).toBe(true) expect(Object.isFrozen(attribution.billingEntity)).toBe(true) diff --git a/apps/sim/lib/logs/folder-scope.test.ts b/apps/sim/lib/logs/folder-scope.test.ts index 5113891ac32..5026f8f12b6 100644 --- a/apps/sim/lib/logs/folder-scope.test.ts +++ b/apps/sim/lib/logs/folder-scope.test.ts @@ -19,7 +19,15 @@ const FOLDERS: FolderRow[] = [ describe('resolveLogFolderScope', () => { beforeEach(() => { resetDbChainMock() - queueTableRows(schemaMock.folder, FOLDERS) + queueTableRows( + schemaMock.folder, + FOLDERS.map((row) => ({ + ...row, + workspaceId: 'workspace-1', + entityType: 'workspace', + entityId: 'workspace-1', + })) + ) }) it('covers the whole subtree of a selected folder', async () => { diff --git a/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts b/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts index d663fc17063..bb248c8b12b 100644 --- a/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts +++ b/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts @@ -12,6 +12,7 @@ import { billingStorageMock, billingStorageMockFns } from '@sim/testing/mocks/bi import { encryptionMock, encryptionMockFns } from '@sim/testing/mocks/encryption.mock' import { getMockLogger } from '@sim/testing/mocks/logger.mock' import { permissionsMock } from '@sim/testing/mocks/permissions.mock' +import { projectMembershipMock } from '@sim/testing/mocks/project-membership.mock' import { realtimeNotifyMock } from '@sim/testing/mocks/realtime-notify.mock' import { redisConfigMockFns } from '@sim/testing/mocks/redis-config.mock' import { @@ -46,6 +47,7 @@ vi.mock('@/lib/api/server/routes/v2-api-key-auth', () => v2ApiKeyAuthModuleMock) vi.mock('@/lib/mothership/chat/delegation', () => ({ mintDelegationToken: async () => 'fixture' })) vi.mock('@/lib/core/security/encryption', () => encryptionMock) vi.mock('@/lib/realtime/notify', () => realtimeNotifyMock) +vi.mock('@/lib/projects/membership', () => projectMembershipMock) vi.mock('@/lib/billing/storage', () => billingStorageMock) vi.mock('@/lib/uploads', () => uploadsMock) vi.mock('@/lib/uploads/core/storage-service', () => storageServiceMock) @@ -137,6 +139,9 @@ const SOURCE = { key: `workspace/${WORKSPACE}/123-abc-source.txt`, userId: 'reader', workspaceId: WORKSPACE, + entityType: 'workspace', + entityId: WORKSPACE, + organizationId: null, folderId: null, context: 'workspace', chatId: null, @@ -179,6 +184,7 @@ function queueRead(file: typeof SOURCE, provenance: WorkspaceFileSecretProvenanc { workspaceId: WORKSPACE, fileId: file.id, + ownership: file, workspaceOrganizationId: null, allowPersonalApiKeys: true, billedAccountUserId: 'owner', diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager-page.test.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager-page.test.ts index 7562705f566..22634ad4b9b 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager-page.test.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager-page.test.ts @@ -1,5 +1,6 @@ -import { dbChainMockFns } from '@sim/testing' +import { dbChainMockFns, resetDbChainMock } from '@sim/testing' import { billingStorageMock } from '@sim/testing/mocks/billing-storage.mock' +import { projectMembershipMock } from '@sim/testing/mocks/project-membership.mock' import { realtimeNotifyMock } from '@sim/testing/mocks/realtime-notify.mock' import { storageServiceMock, storageServiceMockFns } from '@sim/testing/mocks/storage-service.mock' import { uploadsMock } from '@sim/testing/mocks/uploads.mock' @@ -9,16 +10,7 @@ import { } from '@sim/testing/mocks/workspace-file-folders.mock' import { beforeEach, expect, it, vi } from 'vitest' -const mocks = vi.hoisted(() => { - const values = vi.fn() - const insert = vi.fn(() => ({ - values: (metadata: Record) => { - values(metadata) - return { onConflictDoNothing: () => ({ returning: async () => [metadata] }) } - }, - })) - return { values, insert } -}) +vi.mock('@/lib/projects/membership', () => projectMembershipMock) vi.mock('@/lib/billing/storage', () => billingStorageMock) vi.mock('@/lib/folders/locks', () => ({ acquireFolderMutationLock: vi.fn() })) vi.mock('@/lib/realtime/notify', () => realtimeNotifyMock) @@ -41,11 +33,12 @@ workspaceFileFoldersMockFns.mockNormalizeWorkspaceFileItemName.mockImplementatio const mockUpload = storageServiceMockFns.mockUploadFile beforeEach(() => { - dbChainMockFns.transaction.mockImplementation( - async (run: (tx: { insert: typeof mocks.insert }) => Promise) => - run({ insert: mocks.insert }) - ) - mockUpload.mockImplementation(async ({ fileName }: { fileName: string }) => ({ key: fileName })) + resetDbChainMock() + dbChainMockFns.returning.mockImplementation(async () => { + const metadata = dbChainMockFns.values.mock.calls.at(-1)?.[0] + return metadata ? [metadata] : [] + }) + mockUpload.mockImplementation(async ({ customKey }) => ({ key: customKey })) }) it('registers agent-authored Page source as a native Page and preserves editable source', async () => { @@ -68,7 +61,7 @@ it('registers agent-authored Page source as a native Page and preserves editable type: SIM_PAGE_CONTENT_TYPE, size: bytes.length, }) - expect(mocks.values).toHaveBeenCalledWith( + expect(dbChainMockFns.values).toHaveBeenCalledWith( expect.objectContaining({ originalName: 'Team handbook', contentType: SIM_PAGE_CONTENT_TYPE }) ) expect(mockUpload).toHaveBeenCalledWith( @@ -83,7 +76,7 @@ it('keeps a complete HTML document as HTML rather than mislabeling it a native P exactName: true, }) expect(file).toMatchObject({ name: 'report.html', type: 'text/html' }) - expect(mocks.values).toHaveBeenCalledWith( + expect(dbChainMockFns.values).toHaveBeenCalledWith( expect.objectContaining({ originalName: 'report.html', contentType: 'text/html' }) ) }) diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-billing.test.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-billing.test.ts index 5704eb586d6..93a76b0b06e 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-billing.test.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-billing.test.ts @@ -1,5 +1,6 @@ import { dbChainMockFns, resetDbChainMock } from '@sim/testing' import { billingStorageMock, billingStorageMockFns } from '@sim/testing/mocks/billing-storage.mock' +import { projectMembershipMock } from '@sim/testing/mocks/project-membership.mock' import { storageServiceMock, storageServiceMockFns } from '@sim/testing/mocks/storage-service.mock' import { uploadsMock, uploadsMockFns } from '@sim/testing/mocks/uploads.mock' import { @@ -8,6 +9,7 @@ import { } from '@sim/testing/mocks/workspace-file-folders.mock' import { beforeEach, describe, expect, it, vi } from 'vitest' +vi.mock('@/lib/projects/membership', () => projectMembershipMock) vi.mock('@/lib/billing/storage', () => billingStorageMock) vi.mock('@/lib/uploads', () => uploadsMock) @@ -52,19 +54,17 @@ describe('workspace file storage attribution', () => { mockResolveWorkspaceFileFolderTarget.mockResolvedValue(null) mockIncrementStorageUsageForBillingContextInTx.mockResolvedValue(5) mockMaybeNotifyStorageLimitForBillingContext.mockResolvedValue(undefined) - mockUploadFile.mockResolvedValue({ - key: 'workspace/workspace-1/123-abc-note.txt', - }) + mockUploadFile.mockImplementation(async ({ customKey }) => ({ key: customKey })) }) it.each(['external-collaborator', 'personal-api-key-user'])( 'charges the workspace payer while retaining %s as uploader metadata', async (actorUserId) => { dbChainMockFns.returning - .mockResolvedValueOnce([ + .mockImplementationOnce(async () => [ { id: 'file-1', - key: 'workspace/workspace-1/123-abc-note.txt', + key: mockUploadFile.mock.calls.at(-1)?.[0].customKey, userId: actorUserId, workspaceId: 'workspace-1', folderId: null, diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox.test.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox.test.ts index 02d1f1edb35..6be47f94743 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox.test.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox.test.ts @@ -72,12 +72,6 @@ describe('batched workspace file storage cleanup', () => { await expect( enqueueWorkspaceFileStorageCleanups(executor as never, ['a', 'b', 'c']) ).resolves.toEqual(['event-a', 'event-b', 'event-c']) - expect(mockEnqueueOutboxEvents).toHaveBeenNthCalledWith( - 2, - executor, - WORKSPACE_FILE_STORAGE_CLEANUP_OUTBOX_EVENT, - [{ key: 'c' }] - ) }) it('processes every event and leaves failures to the outbox worker without throwing', async () => { diff --git a/apps/sim/lib/uploads/documents/compile.test.ts b/apps/sim/lib/uploads/documents/compile.test.ts index 1870928cad9..7e75fb084aa 100644 --- a/apps/sim/lib/uploads/documents/compile.test.ts +++ b/apps/sim/lib/uploads/documents/compile.test.ts @@ -27,6 +27,7 @@ vi.mock('@/lib/workspace-files/application/read-workspace-file-metadata', () => readWorkspaceFileMetadata: { execute: readWorkspaceFileMetadataMock }, })) vi.mock('@/lib/uploads/documents/compiled-store', () => ({ + compiledArtifactKey: vi.fn(() => 'compiled-artifact-key'), loadCompiledDoc: loadCompiledDocMock, loadPublishedCompiledDoc: vi.fn(), publishCompiledDocArtifact: publishCompiledDocArtifactMock, @@ -154,7 +155,7 @@ describe('collectReferencedFileIds', () => { workspaceId: 'workspace-1', filePrincipal: FILE_PRINCIPAL, }) - ).resolves.toEqual({ + ).resolves.toMatchObject({ buffer: Buffer.from('%PDF-built'), contentType: 'application/pdf', dependsOnReferencedFiles: true, @@ -205,7 +206,7 @@ describe('collectReferencedFileIds', () => { workspaceId: 'workspace-1', filePrincipal: FILE_PRINCIPAL, }) - ).resolves.toEqual({ + ).resolves.toMatchObject({ buffer: Buffer.from('%PDF-cached'), contentType: 'application/pdf', dependsOnReferencedFiles: true, diff --git a/apps/sim/lib/uploads/documents/servable.test.ts b/apps/sim/lib/uploads/documents/servable.test.ts index 8505ce1a297..5f362fd36c6 100644 --- a/apps/sim/lib/uploads/documents/servable.test.ts +++ b/apps/sim/lib/uploads/documents/servable.test.ts @@ -34,6 +34,7 @@ vi.mock('@/lib/workspace-files/application/read-workspace-file-metadata', () => readWorkspaceFileMetadata: { execute: mockReadWorkspaceFileMetadata }, })) vi.mock('@/lib/uploads/documents/compiled-store', () => ({ + compiledArtifactKey: vi.fn(() => 'compiled-artifact-key'), loadCompiledDoc: mockLoadCompiledDoc, loadPublishedCompiledDoc: mockLoadPublishedCompiledDoc, publishCompiledDocArtifact: mockPublishCompiledDocArtifact, @@ -154,7 +155,7 @@ describe('resolveServableDocBytes', () => { filePrincipal: FILE_PRINCIPAL, }) - expect(result).toEqual({ + expect(result).toMatchObject({ buffer: Buffer.from('%PDF-rebuilt'), contentType: 'application/pdf', dependsOnReferencedFiles: true, diff --git a/apps/sim/providers/file-attachments-authorization.test.ts b/apps/sim/providers/file-attachments-authorization.test.ts index ce7e313c401..86adbd5897a 100644 --- a/apps/sim/providers/file-attachments-authorization.test.ts +++ b/apps/sim/providers/file-attachments-authorization.test.ts @@ -87,7 +87,6 @@ describe('provider attachment storage-key authorization', () => { uploadLargeFilesToProvider(request, 'openai', executionContext) ).rejects.toThrow() expect(download).not.toHaveBeenCalled() - expect(metadata).not.toHaveBeenCalled() expect(permission).not.toHaveBeenCalled() } ) diff --git a/packages/testing/src/mocks/project-membership.mock.ts b/packages/testing/src/mocks/project-membership.mock.ts index f079aa2942c..fa953d5591f 100644 --- a/packages/testing/src/mocks/project-membership.mock.ts +++ b/packages/testing/src/mocks/project-membership.mock.ts @@ -2,5 +2,6 @@ import { vi } from 'vitest' /** Project membership locks for isolated workspace resource tests. */ export const projectMembershipMock = { + lockProject: vi.fn(async (): Promise => {}), lockWorkspaceProject: vi.fn(async () => null), } diff --git a/packages/testing/src/mocks/workspaces-utils.mock.ts b/packages/testing/src/mocks/workspaces-utils.mock.ts index 4c2da675994..31ee7dfd511 100644 --- a/packages/testing/src/mocks/workspaces-utils.mock.ts +++ b/packages/testing/src/mocks/workspaces-utils.mock.ts @@ -18,8 +18,8 @@ export class MockWorkspaceBillingAccountRemovalError extends Error { * Controllable mock functions for `@/lib/workspaces/utils`. * * Every loader and transaction helper is a bare `vi.fn()` (returns `undefined`) except the - * account-deletion reassignments, which resolve an empty result - * (`{ reassigned: [], unresolved: [] }`) — nothing to reassign: + * account-deletion planner, which returns `{ changes: [], unresolved: [] }`, and + * reassignments, which return `{ reassigned: [], unresolved: [] }`: * - `mockReassignBilledAccountForUser` * - `mockReassignOwnedWorkspacesForUser` * @@ -37,6 +37,13 @@ export const workspacesUtilsMockFns = { mockListAccessibleWorkspaceRowsForUser: vi.fn(), mockTransferWorkspaceOwnershipToBilledAccountForMemberRemovalTx: vi.fn(), mockReassignWorkflowOwnershipForWorkspaceMemberRemovalTx: vi.fn(), + mockPlanBilledAccountReassignmentsForUser: vi.fn< + ( + departingUserId: string, + executor: unknown, + options?: unknown + ) => Promise<{ changes: unknown[]; unresolved: string[] }> + >(async () => ({ changes: [], unresolved: [] })), mockReassignBilledAccountForUser: vi.fn( async ( _departingUserId: string, @@ -78,6 +85,8 @@ export const workspacesUtilsMock = { workspacesUtilsMockFns.mockTransferWorkspaceOwnershipToBilledAccountForMemberRemovalTx, reassignWorkflowOwnershipForWorkspaceMemberRemovalTx: workspacesUtilsMockFns.mockReassignWorkflowOwnershipForWorkspaceMemberRemovalTx, + planBilledAccountReassignmentsForUser: + workspacesUtilsMockFns.mockPlanBilledAccountReassignmentsForUser, reassignBilledAccountForUser: workspacesUtilsMockFns.mockReassignBilledAccountForUser, reassignOwnedWorkspacesForUser: workspacesUtilsMockFns.mockReassignOwnedWorkspacesForUser, } From 6f8882ecff94a066eccc0404899e3ed842ded55d Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Mon, 5 Oct 2026 13:14:14 -0700 Subject: [PATCH 024/113] refactor: persist file owners with native foreign keys --- apps/sim/app/api/files/authorization.ts | 2 +- .../api/folders/[id]/duplicate/route.test.ts | 3 +- apps/sim/app/api/folders/[id]/route.test.ts | 3 +- apps/sim/app/api/folders/route.test.ts | 6 +- .../api/v1/admin/folders/[id]/export/route.ts | 3 +- .../background/cleanup-soft-deletes.test.ts | 3 +- apps/sim/background/cleanup-soft-deletes.ts | 3 +- .../lib/mapping/resources.test.ts | 6 +- .../sim/lib/billing/storage/payer-transfer.ts | 10 +- .../storage/project-accounting.integration.ts | 46 +-- apps/sim/lib/folders/queries.test.ts | 3 +- apps/sim/lib/folders/scope.ts | 7 +- apps/sim/lib/knowledge/documents/service.ts | 2 +- apps/sim/lib/logs/folder-scope.test.ts | 3 +- .../__integration__/foundation.integration.ts | 4 +- .../private-purge.integration.ts | 10 +- .../__integration__/retention.integration.ts | 16 +- apps/sim/lib/projects/files/purge.ts | 15 +- .../workspace-file-folder-manager.test.ts | 6 +- .../workspace-file-folder-manager.ts | 13 +- .../workspace/workspace-file-manager.ts | 29 +- apps/sim/lib/uploads/core/storage-client.ts | 2 +- apps/sim/lib/workspace-files/locks.ts | 17 + .../lib/workspace-files/ownership-query.ts | 26 +- .../sim/lib/workspace-files/ownership.test.ts | 34 +- apps/sim/lib/workspace-files/ownership.ts | 57 ++-- .../search/chunks.integration.ts | 2 +- .../search/dispatcher.integration.ts | 12 +- .../lib/workspace-files/search/dispatcher.ts | 20 +- .../search/owners.integration.ts | 12 +- apps/sim/lib/workspace-files/search/scope.ts | 4 +- .../db/file-creator-lifetime.integration.ts | 64 ++-- .../db/file-entity-ownership.integration.ts | 187 +++++------ ...le-folder-version-ownership.integration.ts | 179 ++++------ .../migrations/0396_file_entity_ownership.sql | 136 ++------ .../0397_file_folder_version_ownership.sql | 208 +++--------- .../migrations/0398_file_creator_lifetime.sql | 18 +- .../0400_public_share_entity_ownership.sql | 26 +- .../0401_file_search_owner_scope.sql | 16 +- .../db/migrations/meta/0396_snapshot.json | 87 ++--- .../db/migrations/meta/0397_snapshot.json | 304 ++++++++--------- .../db/migrations/meta/0398_snapshot.json | 308 ++++++++--------- .../db/migrations/meta/0399_snapshot.json | 308 ++++++++--------- .../db/migrations/meta/0400_snapshot.json | 308 ++++++++--------- .../db/migrations/meta/0401_snapshot.json | 314 +++++++++--------- .../db/public-share-ownership.integration.ts | 4 +- packages/db/schema.ts | 73 ++-- .../0003_backfill_workspace_storage_usage.ts | 8 +- .../0030_backfill_workspace_file_entities.ts | 88 ----- .../0031_backfill_file_folder_entities.ts | 80 ----- .../0031_validate_file_ownership.ts | 62 ++++ .../0032_backfill_project_file_creators.ts | 4 +- .../0033_backfill_public_share_entities.ts | 8 +- packages/db/script-migrations/index.ts | 6 +- .../src/mocks/schema-tables.generated.ts | 6 +- 55 files changed, 1325 insertions(+), 1856 deletions(-) create mode 100644 apps/sim/lib/workspace-files/locks.ts delete mode 100644 packages/db/script-migrations/0030_backfill_workspace_file_entities.ts delete mode 100644 packages/db/script-migrations/0031_backfill_file_folder_entities.ts create mode 100644 packages/db/script-migrations/0031_validate_file_ownership.ts diff --git a/apps/sim/app/api/files/authorization.ts b/apps/sim/app/api/files/authorization.ts index 7c47bc9375e..a9ef1036560 100644 --- a/apps/sim/app/api/files/authorization.ts +++ b/apps/sim/app/api/files/authorization.ts @@ -129,7 +129,7 @@ export async function verifyFileAccess( const requireWrite = options?.requireWrite ?? false try { const canonical = await getFileMetadataByKey(cloudKey, undefined, { includeDeleted: true }) - if (canonical?.entityType === 'project' || canonical?.context === 'project') return false + if (canonical?.projectId != null || canonical?.context === 'project') return false const keyContext = inferContextFromKey(cloudKey) /** Organization logos are changed only through the organization-authorized upload lifecycle. */ if (keyContext === 'organization-logos') return !requireWrite diff --git a/apps/sim/app/api/folders/[id]/duplicate/route.test.ts b/apps/sim/app/api/folders/[id]/duplicate/route.test.ts index 99866531e6e..3b372ece236 100644 --- a/apps/sim/app/api/folders/[id]/duplicate/route.test.ts +++ b/apps/sim/app/api/folders/[id]/duplicate/route.test.ts @@ -65,8 +65,7 @@ function folderRow(overrides: Record = {}) { return { id: SOURCE_FOLDER_ID, resourceType: 'workflow', - entityType: null, - entityId: null, + projectId: null, name: 'Source', userId: TEST_USER.id, workspaceId: WORKSPACE_ID, diff --git a/apps/sim/app/api/folders/[id]/route.test.ts b/apps/sim/app/api/folders/[id]/route.test.ts index 6488bc07424..91375eb088a 100644 --- a/apps/sim/app/api/folders/[id]/route.test.ts +++ b/apps/sim/app/api/folders/[id]/route.test.ts @@ -38,8 +38,7 @@ const mockFolder = { name: 'Test Folder', userId: TEST_USER.id, workspaceId: 'workspace-123', - entityType: null, - entityId: null, + projectId: null, parentId: null, color: '#6B7280', sortOrder: 1, diff --git a/apps/sim/app/api/folders/route.test.ts b/apps/sim/app/api/folders/route.test.ts index f0c1b6ab8a0..3e9b1e54a0c 100644 --- a/apps/sim/app/api/folders/route.test.ts +++ b/apps/sim/app/api/folders/route.test.ts @@ -82,8 +82,7 @@ describe('Folders API Route', () => { name: 'Test Folder 1', userId: 'user-123', workspaceId: 'workspace-123', - entityType: null, - entityId: null, + projectId: null, parentId: null, color: '#6B7280', isExpanded: true, @@ -96,8 +95,7 @@ describe('Folders API Route', () => { name: 'Test Folder 2', userId: 'user-123', workspaceId: 'workspace-123', - entityType: null, - entityId: null, + projectId: null, parentId: 'folder-1', color: '#EF4444', isExpanded: false, diff --git a/apps/sim/app/api/v1/admin/folders/[id]/export/route.ts b/apps/sim/app/api/v1/admin/folders/[id]/export/route.ts index f140b9c5497..529adaabf1d 100644 --- a/apps/sim/app/api/v1/admin/folders/[id]/export/route.ts +++ b/apps/sim/app/api/v1/admin/folders/[id]/export/route.ts @@ -115,8 +115,7 @@ export const GET = withRouteHandler( id: folderTable.id, name: folderTable.name, workspaceId: folderTable.workspaceId, - entityType: folderTable.entityType, - entityId: folderTable.entityId, + projectId: folderTable.projectId, }) .from(folderTable) .where(and(eq(folderTable.id, folderId), eq(folderTable.resourceType, 'workflow'))) diff --git a/apps/sim/background/cleanup-soft-deletes.test.ts b/apps/sim/background/cleanup-soft-deletes.test.ts index 24bcc51b3dd..b49d4c91684 100644 --- a/apps/sim/background/cleanup-soft-deletes.test.ts +++ b/apps/sim/background/cleanup-soft-deletes.test.ts @@ -496,8 +496,7 @@ describe('folder cleanup target', () => { name: 'Reports', workspaceId: 'ws-1', resourceType: 'knowledge_base', - entityType: null, - entityId: null, + projectId: null, }, ]) mockDeduplicateFolderName.mockResolvedValueOnce('Reports (1)') diff --git a/apps/sim/background/cleanup-soft-deletes.ts b/apps/sim/background/cleanup-soft-deletes.ts index 412ebabfa00..e1e8b82c402 100644 --- a/apps/sim/background/cleanup-soft-deletes.ts +++ b/apps/sim/background/cleanup-soft-deletes.ts @@ -660,8 +660,7 @@ async function reRootActiveFolderChildrenUnguarded( name: folderTable.name, workspaceId: folderTable.workspaceId, resourceType: folderTable.resourceType, - entityType: folderTable.entityType, - entityId: folderTable.entityId, + projectId: folderTable.projectId, }) .from(folderTable) .where(and(inArray(folderTable.parentId, expiredIds), isNull(folderTable.deletedAt))) diff --git a/apps/sim/ee/workspace-forking/lib/mapping/resources.test.ts b/apps/sim/ee/workspace-forking/lib/mapping/resources.test.ts index e4c179d715f..e455eab3b2e 100644 --- a/apps/sim/ee/workspace-forking/lib/mapping/resources.test.ts +++ b/apps/sim/ee/workspace-forking/lib/mapping/resources.test.ts @@ -44,16 +44,14 @@ describe('listForkResourceCandidates', () => { name: 'Reports', parentId: null, workspaceId: 'ws-1', - entityType: 'workspace', - entityId: 'ws-1', + projectId: null, }, { id: 'folder-q3', name: 'Q3 Results', parentId: 'folder-reports', workspaceId: 'ws-1', - entityType: 'workspace', - entityId: 'ws-1', + projectId: null, }, ]) diff --git a/apps/sim/lib/billing/storage/payer-transfer.ts b/apps/sim/lib/billing/storage/payer-transfer.ts index 94a2dfc41fb..61d004b21d3 100644 --- a/apps/sim/lib/billing/storage/payer-transfer.ts +++ b/apps/sim/lib/billing/storage/payer-transfer.ts @@ -869,19 +869,15 @@ async function getExactProjectStorageBytes(tx: DbTransaction, projectIds: string if (!projectIds.length) return bytesById const totals = await tx .select({ - projectId: workspaceFiles.entityId, + projectId: workspaceFiles.projectId, bytes: sql`coalesce(sum(${workspaceFiles.sizeBytes}), 0)::text`, invalidCount: sql`count(*) FILTER (WHERE ${workspaceFiles.sizeBytes} IS NULL OR ${workspaceFiles.sizeBytes} < 0)::integer`, }) .from(workspaceFiles) .where( - and( - eq(workspaceFiles.entityType, 'project'), - eq(workspaceFiles.context, 'project'), - inArray(workspaceFiles.entityId, projectIds) - ) + and(eq(workspaceFiles.context, 'project'), inArray(workspaceFiles.projectId, projectIds)) ) - .groupBy(workspaceFiles.entityId) + .groupBy(workspaceFiles.projectId) for (const total of totals) { if (!total.projectId || total.invalidCount > 0) throw new Error('Project has invalid canonical size metadata') diff --git a/apps/sim/lib/billing/storage/project-accounting.integration.ts b/apps/sim/lib/billing/storage/project-accounting.integration.ts index 1024a42c816..2eafa039fb0 100644 --- a/apps/sim/lib/billing/storage/project-accounting.integration.ts +++ b/apps/sim/lib/billing/storage/project-accounting.integration.ts @@ -109,7 +109,7 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' entity_id text NOT NULL, permission_type text NOT NULL); CREATE TABLE permission_group (id text PRIMARY KEY, organization_id text, config jsonb, updated_at timestamp); - CREATE TABLE workspace_files (id text PRIMARY KEY, entity_type text, entity_id text, + CREATE TABLE workspace_files (id text PRIMARY KEY, project_id text, workspace_id text, context text NOT NULL, size_bytes bigint, deleted_at timestamp); CREATE TABLE workspace_file_version (id text PRIMARY KEY, file_id text REFERENCES workspace_files(id) ON DELETE CASCADE, size_bytes bigint NOT NULL); @@ -195,9 +195,9 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' await sql`UPDATE workspace SET organization_id = NULL, billed_account_user_id = 'user-b', storage_used_bytes = 100 WHERE id = 'workspace-a'` await sql`UPDATE user_stats SET storage_used_bytes = 50 WHERE user_id = 'user-b'` - await sql`INSERT INTO workspace_files (id, entity_type, entity_id, workspace_id, context, size_bytes) - VALUES ('project-file', 'project', 'project-a', NULL, 'project', 40), - ('workspace-file', 'workspace', 'workspace-a', 'workspace-a', 'workspace', 100)` + await sql`INSERT INTO workspace_files (id, project_id, workspace_id, context, size_bytes) + VALUES ('project-file', 'project-a', NULL, 'project', 40), + ('workspace-file', NULL, 'workspace-a', 'workspace', 100)` await database.transaction((tx) => changeProjectAndWorkspaceStoragePayersInTx(tx, { projectChanges: [ @@ -242,9 +242,9 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' storage_used_bytes = 20 WHERE id = 'workspace-a'` await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('project-a', 'workspace-a')` await sql`UPDATE user_stats SET storage_used_bytes = CASE user_id WHEN 'user-a' THEN 40 ELSE 20 END` - await sql`INSERT INTO workspace_files (id, entity_type, entity_id, workspace_id, context, size_bytes) - VALUES ('project-file', 'project', 'project-a', NULL, 'project', 40), - ('workspace-file', 'workspace', 'workspace-a', 'workspace-a', 'workspace', 20)` + await sql`INSERT INTO workspace_files (id, project_id, workspace_id, context, size_bytes) + VALUES ('project-file', 'project-a', NULL, 'project', 40), + ('workspace-file', NULL, 'workspace-a', 'workspace', 20)` await database.transaction((tx) => transferWorkspaceProjects(tx, ['workspace-a'], 'organization-a', undefined, [ { @@ -283,8 +283,8 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' await sql`INSERT INTO permissions (id, user_id, entity_type, entity_id, permission_type) VALUES ('admin', 'user-b', 'workspace', 'workspace-a', 'admin')` await sql`UPDATE user_stats SET storage_used_bytes = 20 WHERE user_id = 'user-a'` - await sql`INSERT INTO workspace_files (id, entity_type, entity_id, workspace_id, context, size_bytes) - VALUES ('workspace-file', 'workspace', 'workspace-a', 'workspace-a', 'workspace', 20)` + await sql`INSERT INTO workspace_files (id, project_id, workspace_id, context, size_bytes) + VALUES ('workspace-file', NULL, 'workspace-a', 'workspace', 20)` const written = createDeferred() const releaseWrite = createDeferred() const teardownPid = createDeferred() @@ -294,8 +294,8 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' organizationId: null, billingEntity: { type: 'user', id: 'user-a' }, }) - await tx.execute(query`INSERT INTO workspace_files (id, entity_type, entity_id, context, size_bytes) - VALUES ('project-file', 'project', 'project-a', 'project', 40)`) + await tx.execute(query`INSERT INTO workspace_files (id, project_id, context, size_bytes) + VALUES ('project-file', 'project-a', 'project', 40)`) await prepared.applyDelta(40) written.resolve() await releaseWrite.promise @@ -341,8 +341,8 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' ...['project-a', 'project-b'].map((projectId) => database.transaction(async (tx) => { const prepared = await prepareProjectStorageMutationInTx(tx, context(projectId, 60)) - await tx.execute(query`INSERT INTO workspace_files (id, entity_type, entity_id, context, size_bytes) - VALUES (${projectId}, 'project', ${projectId}, 'project', 40)`) + await tx.execute(query`INSERT INTO workspace_files (id, project_id, context, size_bytes) + VALUES (${projectId}, ${projectId}, 'project', 40)`) await prepared.applyDelta(40) }) ), @@ -404,7 +404,7 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' await database.transaction(async (tx) => { const prepared = await prepareProjectStorageMutationInTx(tx, context()) await tx.execute( - query`INSERT INTO workspace_files VALUES ('file', 'project', 'project-a', NULL, 'project', 100, NULL)` + query`INSERT INTO workspace_files VALUES ('file', 'project-a', NULL, 'project', 100, NULL)` ) await tx.execute(query`INSERT INTO workspace_file_version VALUES ('version', 'file', 500)`) await prepared.applyDelta(100) @@ -442,9 +442,9 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' 'moves exact Project heads once across payers, including archived heads but excluding versions', async () => { await sql`INSERT INTO workspace_files VALUES - ('active', 'project', 'project-a', NULL, 'project', 40, NULL), - ('archived', 'project', 'project-a', NULL, 'project', 60, now()), - ('other', 'project', 'project-b', NULL, 'project', 25, NULL)` + ('active', 'project-a', NULL, 'project', 40, NULL), + ('archived', 'project-a', NULL, 'project', 60, now()), + ('other', 'project-b', NULL, 'project', 25, NULL)` await sql`INSERT INTO workspace_file_version VALUES ('version', 'active', 900)` await sql`UPDATE organization SET storage_used_bytes = 125 WHERE id = 'organization-a'` await database.transaction((tx) => @@ -485,10 +485,10 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' 'reconciliation adds Project heads once and refuses incomplete canonical size metadata', async () => { await sql`INSERT INTO workspace_files VALUES - ('workspace-file', 'workspace', 'workspace-a', 'workspace-a', 'workspace', 20, NULL), - ('project-file', 'project', 'project-a', NULL, 'project', 40, NULL), - ('archived', 'project', 'project-a', NULL, 'project', 60, now()), - ('chat', NULL, NULL, 'workspace-a', 'mothership', 999, NULL)` + ('workspace-file', NULL, 'workspace-a', 'workspace', 20, NULL), + ('project-file', 'project-a', NULL, 'project', 40, NULL), + ('archived', 'project-a', NULL, 'project', 60, now()), + ('chat', NULL, 'workspace-a', 'mothership', 999, NULL)` await sql`INSERT INTO workspace_file_version VALUES ('version', 'project-file', 900)` const store = createPostgresStorageReconciliationStore(sql) await store.reconcileWorkspaces(['workspace-a']) @@ -512,7 +512,7 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' const write = database.transaction(async (tx) => { const prepared = await prepareProjectStorageMutationInTx(tx, context()) await tx.execute( - query`INSERT INTO workspace_files VALUES ('file', 'project', 'project-a', NULL, 'project', 40, NULL)` + query`INSERT INTO workspace_files VALUES ('file', 'project-a', NULL, 'project', 40, NULL)` ) await prepared.applyDelta(40) changed.resolve() @@ -549,7 +549,7 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' const write = database.transaction(async (tx) => { const prepared = await prepareProjectStorageMutationInTx(tx, context()) await tx.execute( - query`INSERT INTO workspace_files VALUES ('file', 'project', 'project-a', NULL, 'project', 40, NULL)` + query`INSERT INTO workspace_files VALUES ('file', 'project-a', NULL, 'project', 40, NULL)` ) await prepared.applyDelta(40) changed.resolve() diff --git a/apps/sim/lib/folders/queries.test.ts b/apps/sim/lib/folders/queries.test.ts index 8b8e1879e14..3cd6fe6f199 100644 --- a/apps/sim/lib/folders/queries.test.ts +++ b/apps/sim/lib/folders/queries.test.ts @@ -18,8 +18,7 @@ const ROW = { name: 'Reports', userId: 'u-1', workspaceId: 'ws-1', - entityType: null, - entityId: null, + projectId: null, parentId: null, locked: false, sortOrder: 0, diff --git a/apps/sim/lib/folders/scope.ts b/apps/sim/lib/folders/scope.ts index 76f4aa02e09..1c47ced35fd 100644 --- a/apps/sim/lib/folders/scope.ts +++ b/apps/sim/lib/folders/scope.ts @@ -56,9 +56,9 @@ export interface FolderIdScope { includeRootItems: boolean } -type FolderScope = Pick +type FolderScope = Pick -/** Narrows legacy and entity-owned rows to one canonical workspace folder. */ +/** Narrows persisted ownership to one canonical workspace folder. */ export function isWorkspaceFolder( row: T, workspaceId?: string @@ -67,8 +67,7 @@ export function isWorkspaceFolder( typeof row.workspaceId === 'string' && row.workspaceId.length > 0 && (workspaceId === undefined || row.workspaceId === workspaceId) && - ((row.entityType === null && row.entityId === null) || - (row.entityType === 'workspace' && row.entityId === row.workspaceId)) + row.projectId === null ) } diff --git a/apps/sim/lib/knowledge/documents/service.ts b/apps/sim/lib/knowledge/documents/service.ts index 62db7a25d55..271e5030be7 100644 --- a/apps/sim/lib/knowledge/documents/service.ts +++ b/apps/sim/lib/knowledge/documents/service.ts @@ -679,7 +679,7 @@ function durableSecretProvenanceFromWorkspaceFile( ): DurableSecretProvenance { if (provenance.status === 'unknown') return provenance const sourceUserId = binding.userId - if (!sourceUserId || binding.entityType === 'project') return { status: 'unknown' } + if (!sourceUserId || binding.projectId != null) return { status: 'unknown' } if (provenance.status === 'unrecorded') return EXACT_EMPTY_DURABLE_SECRET_PROVENANCE return { status: 'exact', diff --git a/apps/sim/lib/logs/folder-scope.test.ts b/apps/sim/lib/logs/folder-scope.test.ts index 5026f8f12b6..8be1a00066f 100644 --- a/apps/sim/lib/logs/folder-scope.test.ts +++ b/apps/sim/lib/logs/folder-scope.test.ts @@ -24,8 +24,7 @@ describe('resolveLogFolderScope', () => { FOLDERS.map((row) => ({ ...row, workspaceId: 'workspace-1', - entityType: 'workspace', - entityId: 'workspace-1', + projectId: null, })) ) }) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 8a99b573f4b..a24578c72b6 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -170,7 +170,9 @@ async function fixture(org = true, count = 2) { updatedAt: now, }) } - await db.insert(userStats).values({ id: generateId(), userId: ownerId }) + await db + .insert(userStats) + .values([ownerId, teammateId, outsiderId].map((userId) => ({ id: generateId(), userId }))) const organizationId = org ? generateId() : null if (organizationId) { await db diff --git a/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts b/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts index cbc1fa9f9b1..6ec9fa7920a 100644 --- a/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts +++ b/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts @@ -134,16 +134,14 @@ async function fixture() { userId, resourceType: 'file', name: 'Docs', - entityType: 'project', - entityId: projectId, + projectId, }) const revision = new Date() await db.insert(workspaceFiles).values( fileIds.map((id, index) => ({ id, userId, - entityType: 'project', - entityId: projectId, + projectId, context: 'project', key: keys[index], folderId, @@ -560,9 +558,7 @@ afterAll(async () => { try { for (const f of fixtures) { await db.delete(workspaceFiles).where(inArray(workspaceFiles.id, f.fileIds)) - await db - .delete(folder) - .where(and(eq(folder.entityType, 'project'), eq(folder.entityId, f.projectId))) + await db.delete(folder).where(eq(folder.projectId, f.projectId)) await deleteWorkspaceFixture(db, eq(workspace.id, f.workspaceId)) await db.delete(uploadSession).where(eq(uploadSession.userId, f.userId)) await db.delete(user).where(eq(user.id, f.userId)) diff --git a/apps/sim/lib/projects/files/__integration__/retention.integration.ts b/apps/sim/lib/projects/files/__integration__/retention.integration.ts index 3e73ea96322..cf74efd130e 100644 --- a/apps/sim/lib/projects/files/__integration__/retention.integration.ts +++ b/apps/sim/lib/projects/files/__integration__/retention.integration.ts @@ -163,8 +163,7 @@ async function seedFile( await db.insert(workspaceFiles).values({ id: fileId, userId: f.creatorId, - entityType: kind, - entityId: ownerId, + projectId: kind === 'project' ? f.projectId : null, context: kind, workspaceId: kind === 'workspace' ? f.workspaceId : null, key: keys[11], @@ -392,8 +391,7 @@ describe('Project file retention follows the current payer in PostgreSQL', () => const childId = generateId() const rootFolderId = generateId() const owner = { - entityType: 'project' as const, - entityId: f.projectId, + projectId: f.projectId, userId: f.creatorId, resourceType: 'file' as const, } @@ -480,14 +478,8 @@ afterAll(async () => { .where( sql`${outboxEvent.payload}->>'key' LIKE ${`project/${f.projectId}/%`} OR ${outboxEvent.payload}->>'key' LIKE ${`workspace/${f.workspaceId}/%`}` ) - await db - .delete(workspaceFiles) - .where( - and(eq(workspaceFiles.entityType, 'project'), eq(workspaceFiles.entityId, f.projectId)) - ) - await db - .delete(folder) - .where(and(eq(folder.entityType, 'project'), eq(folder.entityId, f.projectId))) + await db.delete(workspaceFiles).where(eq(workspaceFiles.projectId, f.projectId)) + await db.delete(folder).where(eq(folder.projectId, f.projectId)) await deleteWorkspaceFixture(db, eq(workspace.id, f.workspaceId)) await db .delete(subscription) diff --git a/apps/sim/lib/projects/files/purge.ts b/apps/sim/lib/projects/files/purge.ts index b76c8f98113..8a77ad8e4b5 100644 --- a/apps/sim/lib/projects/files/purge.ts +++ b/apps/sim/lib/projects/files/purge.ts @@ -1,9 +1,9 @@ import { folder, publicShare, workspaceFiles, workspaceFileVersion } from '@sim/db/schema' import { and, asc, eq, gt, inArray } from 'drizzle-orm' import type { DbTransaction } from '@/lib/db/types' -import { acquireFolderMutationLock } from '@/lib/folders/locks' import { retireProjectUploadsInTx } from '@/lib/projects/files/prefix-cleanup' import { enqueueWorkspaceFileStorageCleanups } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' +import { lockFileDirectories } from '@/lib/workspace-files/locks' const PURGE_BATCH_SIZE = 500 @@ -17,10 +17,9 @@ export async function purgeProjectFilesInTx( expectedBillableBytes: number ): Promise { await retireProjectUploadsInTx(tx, projectId) - await acquireFolderMutationLock(tx, `project:${projectId}`, 'file') + await lockFileDirectories(tx, [{ entityType: 'project', entityId: projectId }]) const ownedFiles = and( - eq(workspaceFiles.entityType, 'project'), - eq(workspaceFiles.entityId, projectId), + eq(workspaceFiles.projectId, projectId), eq(workspaceFiles.context, 'project') ) const eventIds: string[] = [] @@ -68,13 +67,7 @@ export async function purgeProjectFilesInTx( throw new Error('Project storage changed during retirement') const removedFolders = await tx .delete(folder) - .where( - and( - eq(folder.entityType, 'project'), - eq(folder.entityId, projectId), - eq(folder.resourceType, 'file') - ) - ) + .where(and(eq(folder.projectId, projectId), eq(folder.resourceType, 'file'))) .returning({ id: folder.id }) if (removedFolders.length) await tx.delete(publicShare).where( diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.test.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.test.ts index a2f71225829..d3b04981506 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.test.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.test.ts @@ -100,8 +100,7 @@ describe('listWorkspaceFileFolders', () => { const activeParent = { id: 'parent-1', resourceType: 'file', - entityType: null, - entityId: null, + projectId: null, workspaceId: 'workspace-1', userId: 'user-1', name: 'Engineering', @@ -188,8 +187,7 @@ describe('relocateWorkspaceFileFolderByPath', () => { const source = { id: 'folder-source', resourceType: 'file', - entityType: null, - entityId: null, + projectId: null, workspaceId: 'workspace-1', userId: 'user-1', name: 'xp-files', diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts index 612d8e9de34..88bc49dffb8 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts @@ -8,7 +8,6 @@ import { type ListSortOrder, listOrderBy } from '@/lib/api/list-query' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbOrTx, DbTransaction } from '@/lib/db/types' import { FolderCollectionLimitExceededError } from '@/lib/folders/errors' -import { acquireFolderMutationLock } from '@/lib/folders/locks' import { deduplicateFolderName, deduplicateFolderNameInScope } from '@/lib/folders/naming' import { buildFolderPath, @@ -33,7 +32,8 @@ import { folderPathSegments, } from '@/lib/workspace-files/folder-display-path' import { MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS } from '@/lib/workspace-files/limits' -import type { EditableFileOwner } from '@/lib/workspace-files/ownership' +import { lockFileDirectories } from '@/lib/workspace-files/locks' +import { type EditableFileOwner, editableFileOwnerColumns } from '@/lib/workspace-files/ownership' import { fileFolderOwnerCondition, fileOwnerCondition } from '@/lib/workspace-files/ownership-query' import { getWorkspaceWithOwner } from '@/lib/workspaces/permissions/utils' @@ -277,13 +277,12 @@ export function workspaceFileNameFolderCondition(folderId?: string | null) { async function acquireWorkspaceFileFolderMutationLock(tx: DbTransaction, workspaceId: string) { await lockWorkspaceProject(tx, workspaceId) - await acquireFolderMutationLock(tx, workspaceId, FILE_FOLDER_RESOURCE_TYPE) + await lockFileDirectories(tx, [{ entityType: 'workspace', entityId: workspaceId }]) } async function acquireFileFolderMutationLock(tx: DbTransaction, owner: EditableFileOwner) { if (owner.entityType === 'workspace') await lockWorkspaceProject(tx, owner.entityId) - const ownerKey = owner.entityType === 'workspace' ? owner.entityId : `project:${owner.entityId}` - await acquireFolderMutationLock(tx, ownerKey, FILE_FOLDER_RESOURCE_TYPE) + await lockFileDirectories(tx, [owner]) } export function buildWorkspaceFileFolderPathMap( @@ -695,9 +694,7 @@ async function createFileFolder( resourceType: FILE_FOLDER_RESOURCE_TYPE, name, userId: params.userId, - entityType: params.owner.entityType, - entityId: params.owner.entityId, - workspaceId: params.owner.entityType === 'workspace' ? params.owner.entityId : null, + ...editableFileOwnerColumns(params.owner), parentId, sortOrder: params.sortOrder ?? 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 2e9262a88aa..f9e5a6c017f 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -55,7 +55,6 @@ 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, DbTransaction } from '@/lib/db/types' -import { acquireFolderMutationLock } from '@/lib/folders/locks' import { parseFolderPath } from '@/lib/folders/paths' import { loadActiveFolderPathIndex, resolveFolderPathFromIndex } from '@/lib/folders/queries' import type { FolderIdScope } from '@/lib/folders/scope' @@ -104,8 +103,10 @@ 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 { lockFileDirectories } from '@/lib/workspace-files/locks' import { type EditableFileOwner, + editableFileOwnerColumns, type FileOwner, resolveFileOwner, } from '@/lib/workspace-files/ownership' @@ -321,9 +322,7 @@ async function insertFileMetadataInTx( .insert(workspaceFiles) .values({ ...omit(metadata, ['size']), - entityType: owner.entityType, - entityId: owner.entityId, - workspaceId: owner.entityType === 'workspace' ? owner.entityId : null, + ...editableFileOwnerColumns(owner), sizeBytes: metadata.size, context: owner.entityType, displayName: metadata.originalName, @@ -626,7 +625,7 @@ export async function uploadWorkspaceFile( try { finalized = await db.transaction(async (tx) => { await lockWorkspaceProject(tx, workspaceId) - await acquireFolderMutationLock(tx, workspaceId, 'file') + await lockFileDirectories(tx, [{ entityType: 'workspace', entityId: workspaceId }]) let activeFolderId: string | null if (options?.folderPath !== undefined) { const folderIndex = await loadActiveFolderPathIndex(workspaceId, 'file', tx) @@ -897,7 +896,7 @@ export async function registerUploadedWorkspaceFile(params: { const finalized = await db.transaction(async (tx) => { await lockWorkspaceProject(tx, workspaceId) - await acquireFolderMutationLock(tx, workspaceId, 'file') + await lockFileDirectories(tx, [{ entityType: 'workspace', entityId: workspaceId }]) const activeFolderId = await assertWorkspaceFileFolderTarget(workspaceId, folderId, tx) const inserted = await insertWorkspaceFileMetadataInTx(tx, { id: fileId, @@ -1878,8 +1877,7 @@ export async function loadActiveWorkspaceFileContext( allowPersonalApiKeys: workspace.allowPersonalApiKeys, billedAccountUserId: workspace.billedAccountUserId, ownership: { - entityType: workspaceFiles.entityType, - entityId: workspaceFiles.entityId, + projectId: workspaceFiles.projectId, context: workspaceFiles.context, workspaceId: workspaceFiles.workspaceId, organizationId: workspaceFiles.organizationId, @@ -1929,8 +1927,7 @@ export async function loadWorkspaceFileLifecycleContext( billedAccountUserId: workspace.billedAccountUserId, deletedAt: workspaceFiles.deletedAt, ownership: { - entityType: workspaceFiles.entityType, - entityId: workspaceFiles.entityId, + projectId: workspaceFiles.projectId, context: workspaceFiles.context, workspaceId: workspaceFiles.workspaceId, organizationId: workspaceFiles.organizationId, @@ -2538,11 +2535,7 @@ async function renameFileInTx( ): Promise { const name = normalizeWorkspaceFileItemName(newName.trim(), 'File') if (owner.entityType === 'workspace') await lockWorkspaceProject(tx, owner.entityId) - await acquireFolderMutationLock( - tx, - owner.entityType === 'workspace' ? owner.entityId : `project:${owner.entityId}`, - 'file' - ) + await lockFileDirectories(tx, [owner]) const [file] = await tx .select() .from(workspaceFiles) @@ -2729,11 +2722,7 @@ async function restoreFileInTx( fileId: string ): Promise { if (owner.entityType === 'workspace') await lockWorkspaceProject(tx, owner.entityId) - await acquireFolderMutationLock( - tx, - owner.entityType === 'workspace' ? owner.entityId : `project:${owner.entityId}`, - 'file' - ) + await lockFileDirectories(tx, [owner]) const [file] = await tx .select() .from(workspaceFiles) diff --git a/apps/sim/lib/uploads/core/storage-client.ts b/apps/sim/lib/uploads/core/storage-client.ts index 378ac3fb749..485841ec286 100644 --- a/apps/sim/lib/uploads/core/storage-client.ts +++ b/apps/sim/lib/uploads/core/storage-client.ts @@ -25,7 +25,7 @@ export async function getFileMetadata( if (metadataRecord) { return { - ...(metadataRecord.entityType !== 'project' && + ...(metadataRecord.projectId == null && metadataRecord.context !== 'project' && metadataRecord.userId ? { userId: metadataRecord.userId } diff --git a/apps/sim/lib/workspace-files/locks.ts b/apps/sim/lib/workspace-files/locks.ts new file mode 100644 index 00000000000..5ff62dfad40 --- /dev/null +++ b/apps/sim/lib/workspace-files/locks.ts @@ -0,0 +1,17 @@ +import type { DbTransaction } from '@/lib/db/types' +import { acquireFolderMutationLock } from '@/lib/folders/locks' +import type { EditableFileOwner } from '@/lib/workspace-files/ownership' + +/** Locks owner directories in a stable order after lifecycle and billing locks are held. */ +export async function lockFileDirectories( + tx: DbTransaction, + owners: readonly EditableFileOwner[] +): Promise { + // These keys also coordinate with deployed workspace writers and folder hierarchy triggers. + const keys = owners.map((owner) => + owner.entityType === 'workspace' ? owner.entityId : `project:${owner.entityId}` + ) + for (const key of [...new Set(keys)].sort()) { + await acquireFolderMutationLock(tx, key, 'file') + } +} diff --git a/apps/sim/lib/workspace-files/ownership-query.ts b/apps/sim/lib/workspace-files/ownership-query.ts index b42c0287847..fdfb46ea0c3 100644 --- a/apps/sim/lib/workspace-files/ownership-query.ts +++ b/apps/sim/lib/workspace-files/ownership-query.ts @@ -1,13 +1,12 @@ import { folder, workspaceFiles } from '@sim/db/schema' -import { and, eq, inArray, isNull, or } from 'drizzle-orm' +import { and, eq, inArray, isNull } from 'drizzle-orm' import type { EditableFileOwner } from '@/lib/workspace-files/ownership' -/** Selects only coherent canonical ownership, including the audited workspace expansion fallback. */ +/** Selects only coherent canonical ownership, independent of creator and purpose. */ export function fileOwnerCondition(owner: EditableFileOwner, includeChatUploads = false) { if (owner.entityType === 'project') { return and( - eq(workspaceFiles.entityType, 'project'), - eq(workspaceFiles.entityId, owner.entityId), + eq(workspaceFiles.projectId, owner.entityId), eq(workspaceFiles.context, 'project'), isNull(workspaceFiles.workspaceId), isNull(workspaceFiles.organizationId), @@ -20,10 +19,7 @@ export function fileOwnerCondition(owner: EditableFileOwner, includeChatUploads includeChatUploads ? inArray(workspaceFiles.context, ['workspace', 'mothership']) : eq(workspaceFiles.context, 'workspace'), - or( - and(isNull(workspaceFiles.entityType), isNull(workspaceFiles.entityId)), - and(eq(workspaceFiles.entityType, 'workspace'), eq(workspaceFiles.entityId, owner.entityId)) - ) + isNull(workspaceFiles.projectId) ) } @@ -32,17 +28,7 @@ export function fileFolderOwnerCondition(owner: EditableFileOwner) { return and( eq(folder.resourceType, 'file'), owner.entityType === 'workspace' - ? and( - eq(folder.workspaceId, owner.entityId), - or( - and(isNull(folder.entityType), isNull(folder.entityId)), - and(eq(folder.entityType, 'workspace'), eq(folder.entityId, owner.entityId)) - ) - ) - : and( - isNull(folder.workspaceId), - eq(folder.entityType, 'project'), - eq(folder.entityId, owner.entityId) - ) + ? eq(folder.workspaceId, owner.entityId) + : eq(folder.projectId, owner.entityId) ) } diff --git a/apps/sim/lib/workspace-files/ownership.test.ts b/apps/sim/lib/workspace-files/ownership.test.ts index 2b0f642495e..a2a75d6af1d 100644 --- a/apps/sim/lib/workspace-files/ownership.test.ts +++ b/apps/sim/lib/workspace-files/ownership.test.ts @@ -2,8 +2,7 @@ import { describe, expect, it } from 'vitest' import { resolveFileOwner } from '@/lib/workspace-files/ownership' const legacyWorkspaceFile = { - entityType: null, - entityId: null, + projectId: null, context: 'workspace', workspaceId: 'workspace-a', organizationId: null, @@ -14,37 +13,23 @@ const legacyWorkspaceFile = { const projectFile = { ...legacyWorkspaceFile, - entityType: 'project', - entityId: 'project-a', + projectId: 'project-a', context: 'project', workspaceId: null, } describe('file ownership isolation', () => { - it('keeps audited legacy workspace ownership while its pair awaits backfill', () => { + it('keeps audited workspace ownership independent of Project support', () => { expect(resolveFileOwner(legacyWorkspaceFile)).toEqual({ entityType: 'workspace', entityId: 'workspace-a', }) }) - it.each([ - { entityType: 'workspace', entityId: null }, - { entityType: null, entityId: 'workspace-a' }, - { entityType: 'workspace', entityId: '' }, - { entityType: 'unrecognized', entityId: 'workspace-a' }, - { entityType: 'workspace', entityId: 'workspace-b' }, - { entityType: 'user', entityId: 'uploader-a' }, - ])('rejects a conflicting or incomplete pair %j', (binding) => { - expect(resolveFileOwner({ ...legacyWorkspaceFile, ...binding })).toBeNull() - }) - it('does not change shared ownership when the uploader changes', () => { expect( resolveFileOwner({ ...legacyWorkspaceFile, - entityType: 'workspace', - entityId: 'workspace-a', userId: 'different-uploader', }) ).toEqual({ entityType: 'workspace', entityId: 'workspace-a' }) @@ -66,14 +51,12 @@ describe('file ownership isolation', () => { (context) => { const file = { ...legacyWorkspaceFile, context, workspaceId: null } expect(resolveFileOwner(file)).toBeNull() - expect(resolveFileOwner({ ...file, entityType: 'user', entityId: file.userId })).toBeNull() } ) it.each([ - { entityType: null, entityId: null }, - { entityType: 'workspace', entityId: 'workspace-a' }, - { entityType: 'project', entityId: '' }, + { projectId: null }, + { projectId: '' }, { workspaceId: 'workspace-a' }, { organizationId: 'organization-a' }, { chatId: 'chat-a' }, @@ -93,9 +76,6 @@ describe('file ownership isolation', () => { entityType: 'organization', entityId: 'organization-a', }) - expect( - resolveFileOwner({ ...file, entityType: 'organization', entityId: 'organization-b' }) - ).toBeNull() }) it.each(['copilot', 'profile-pictures'])( @@ -103,9 +83,7 @@ describe('file ownership isolation', () => { (context) => { const file = { ...legacyWorkspaceFile, context, workspaceId: null } expect(resolveFileOwner(file)).toEqual({ entityType: 'user', entityId: 'uploader-a' }) - expect( - resolveFileOwner({ ...file, entityType: 'user', entityId: 'different-person' }) - ).toBeNull() + expect(resolveFileOwner({ ...file, userId: null })).toBeNull() } ) }) diff --git a/apps/sim/lib/workspace-files/ownership.ts b/apps/sim/lib/workspace-files/ownership.ts index 42a119a8c28..7b4ac03aaa8 100644 --- a/apps/sim/lib/workspace-files/ownership.ts +++ b/apps/sim/lib/workspace-files/ownership.ts @@ -15,11 +15,27 @@ export interface EditableFileOwner { type PersistedFileOwnership = Pick< WorkspaceFileRow, - 'entityType' | 'entityId' | 'context' | 'workspaceId' | 'organizationId' | 'userId' | 'chatId' + 'projectId' | 'context' | 'workspaceId' | 'organizationId' | 'userId' | 'chatId' > -function resolveLegacyFileOwner(file: PersistedFileOwnership): FileOwner | null { - if (file.organizationId === null && file.workspaceId) { +/** Maps editable owners to their mutually exclusive persistence columns. */ +export function editableFileOwnerColumns(owner: EditableFileOwner) { + return { + workspaceId: owner.entityType === 'workspace' ? owner.entityId : null, + projectId: owner.entityType === 'project' ? owner.entityId : null, + } +} + +/** Resolves canonical foreign keys without granting access or guessing unclassified contexts. */ +export function resolveFileOwner(file: PersistedFileOwnership): FileOwner | null { + const owners = [file.workspaceId, file.projectId, file.organizationId] + if (owners.some((id) => id === '') || owners.filter((id) => id != null).length > 1) return null + if (file.projectId) { + return file.context === 'project' && file.chatId === null + ? { entityType: 'project', entityId: file.projectId } + : null + } + if (file.workspaceId) { switch (file.context) { case 'workspace': case 'mothership': @@ -29,12 +45,13 @@ function resolveLegacyFileOwner(file: PersistedFileOwnership): FileOwner | null return { entityType: 'workspace', entityId: file.workspaceId } } } - if (file.context === 'knowledge-base' && file.workspaceId === null && file.organizationId) { + if (file.context === 'knowledge-base' && file.organizationId) { return { entityType: 'organization', entityId: file.organizationId } } if ( (file.context === 'copilot' || file.context === 'profile-pictures') && file.workspaceId === null && + file.projectId === null && file.organizationId === null && file.userId ) { @@ -42,35 +59,3 @@ function resolveLegacyFileOwner(file: PersistedFileOwnership): FileOwner | null } return null } - -/** - * Resolves the persisted pair without repairing conflicts or granting access. - * During expansion, an absent pair uses only the audited legacy mappings from - * `workspace_file_legacy_entity`; unclassified contexts remain unresolved. - */ -export function resolveFileOwner(file: PersistedFileOwnership): FileOwner | null { - if (file.entityType === null && file.entityId === null) return resolveLegacyFileOwner(file) - if (file.entityType === null || !file.entityId) return null - - if (file.entityType === 'project') { - if ( - file.context !== 'project' || - file.workspaceId !== null || - file.organizationId !== null || - file.chatId !== null - ) { - return null - } - return { entityType: 'project', entityId: file.entityId } - } - - const legacyOwner = resolveLegacyFileOwner(file) - if ( - !legacyOwner || - legacyOwner.entityType !== file.entityType || - legacyOwner.entityId !== file.entityId - ) { - return null - } - return legacyOwner -} diff --git a/apps/sim/lib/workspace-files/search/chunks.integration.ts b/apps/sim/lib/workspace-files/search/chunks.integration.ts index cf961f536ff..fdaf4790e6b 100644 --- a/apps/sim/lib/workspace-files/search/chunks.integration.ts +++ b/apps/sim/lib/workspace-files/search/chunks.integration.ts @@ -166,7 +166,7 @@ describe('chunked workspace file search on PostgreSQL', () => { await connection`CREATE TABLE workspace (id text PRIMARY KEY)` await connection`CREATE TABLE workspace_files (id text PRIMARY KEY, workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, context text NOT NULL, content_updated_at timestamp NOT NULL, deleted_at timestamp, - original_name text NOT NULL, key text NOT NULL DEFAULT 'key', user_id text NOT NULL DEFAULT 'owner', folder_id text, entity_type text, entity_id text, organization_id text, chat_id text)` + original_name text NOT NULL, key text NOT NULL DEFAULT 'key', user_id text NOT NULL DEFAULT 'owner', folder_id text, project_id text, organization_id text, chat_id text)` for (const migration of [ '0313_puzzling_zodiak.sql', '0358_workspace_file_content_version_precision.sql', diff --git a/apps/sim/lib/workspace-files/search/dispatcher.integration.ts b/apps/sim/lib/workspace-files/search/dispatcher.integration.ts index 5bbb4d44162..ebfd6e86ce4 100644 --- a/apps/sim/lib/workspace-files/search/dispatcher.integration.ts +++ b/apps/sim/lib/workspace-files/search/dispatcher.integration.ts @@ -72,7 +72,7 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { await connection`CREATE TABLE workspace_files ( id text PRIMARY KEY, workspace_id text, context text NOT NULL, deleted_at timestamp, content_updated_at timestamp NOT NULL, - entity_type text, entity_id text, key text NOT NULL DEFAULT 'key' + project_id text, key text NOT NULL DEFAULT 'key' )` await connection`CREATE TABLE workspace_file_search_revision ( file_id text PRIMARY KEY, workspace_id text NOT NULL, @@ -151,15 +151,15 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { entityType: 'workspace' | 'project' = 'workspace' ) { await connection`UPDATE workspace_file_search_backfill SET completed_at = now()` - await connection`INSERT INTO workspace_files (id, workspace_id, context, entity_type, entity_id, content_updated_at) + await connection`INSERT INTO workspace_files (id, workspace_id, context, project_id, content_updated_at) SELECT ${entityType === 'workspace' ? '' : 'project-'} || ${workspaceId} || '-' || lpad(n::text, 6, '0'), - ${entityType === 'workspace' ? workspaceId : null}, ${entityType}, ${entityType}, ${workspaceId}, '2026-09-16' + ${entityType === 'workspace' ? workspaceId : null}, ${entityType}, ${entityType === 'project' ? workspaceId : null}, '2026-09-16' FROM generate_series(1, ${queued + active}) n` await connection`INSERT INTO workspace_file_search_revision (file_id, workspace_id, entity_type, entity_id, source_content_updated_at, status, updated_at, dispatched_at) - SELECT id, workspace_id, entity_type, entity_id, content_updated_at, 'pending', '2026-09-16', + SELECT id, workspace_id, context, coalesce(project_id, workspace_id), content_updated_at, 'pending', '2026-09-16', CASE WHEN row_number() OVER (ORDER BY id DESC) <= ${active} THEN now() ELSE NULL END - FROM workspace_files WHERE entity_type = ${entityType} AND entity_id = ${workspaceId}` + FROM workspace_files WHERE context = ${entityType} AND coalesce(project_id, workspace_id) = ${workspaceId}` await connection`INSERT INTO file_search_dispatch_queue (entity_type, entity_id, enqueued_at, updated_at) VALUES (${entityType}, ${workspaceId}, now(), now())` } @@ -310,7 +310,7 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { ) expect(walk).toBeDefined() expect(walk?.query).toContain( - 'coalesce("workspace_files"."entity_id", "workspace_files"."workspace_id"), "workspace_files"."id") >' + 'coalesce("workspace_files"."project_id", "workspace_files"."workspace_id"), "workspace_files"."id") >' ) const plan = await connection.begin(async (tx) => { diff --git a/apps/sim/lib/workspace-files/search/dispatcher.ts b/apps/sim/lib/workspace-files/search/dispatcher.ts index becbb76d505..9426b02d27e 100644 --- a/apps/sim/lib/workspace-files/search/dispatcher.ts +++ b/apps/sim/lib/workspace-files/search/dispatcher.ts @@ -213,24 +213,24 @@ async function seedBackfillPage(tx: DbTransaction, now: Date): Promise { const rows = await tx .select({ - entityType: sql`coalesce(${workspaceFiles.entityType}, 'workspace')`, - entityId: sql`coalesce(${workspaceFiles.entityId}, ${workspaceFiles.workspaceId})`, + entityType: sql`CASE WHEN ${workspaceFiles.projectId} IS NOT NULL THEN 'project' ELSE 'workspace' END`, + entityId: sql`coalesce(${workspaceFiles.projectId}, ${workspaceFiles.workspaceId})`, fileId: workspaceFiles.id, sourceContentUpdatedAt: workspaceFiles.contentUpdatedAt, }) .from(workspaceFiles) .where( and( - sql`((${workspaceFiles.context} = 'workspace' AND ${workspaceFiles.workspaceId} IS NOT NULL) OR (${workspaceFiles.context} = 'project' AND ${workspaceFiles.entityType} = 'project' AND ${workspaceFiles.entityId} IS NOT NULL))`, + sql`((${workspaceFiles.context} = 'workspace' AND ${workspaceFiles.workspaceId} IS NOT NULL) OR (${workspaceFiles.context} = 'project' AND ${workspaceFiles.projectId} IS NOT NULL))`, isNull(workspaceFiles.deletedAt), afterEntityType && afterEntityId && afterFileId - ? sql`(coalesce(${workspaceFiles.entityType}, 'workspace'), coalesce(${workspaceFiles.entityId}, ${workspaceFiles.workspaceId}), ${workspaceFiles.id}) > (${afterEntityType}, ${afterEntityId}, ${afterFileId})` + ? sql`(CASE WHEN ${workspaceFiles.projectId} IS NOT NULL THEN 'project' ELSE 'workspace' END, coalesce(${workspaceFiles.projectId}, ${workspaceFiles.workspaceId}), ${workspaceFiles.id}) > (${afterEntityType}, ${afterEntityId}, ${afterFileId})` : undefined ) ) .orderBy( - sql`coalesce(${workspaceFiles.entityType}, 'workspace')`, - sql`coalesce(${workspaceFiles.entityId}, ${workspaceFiles.workspaceId})`, + sql`CASE WHEN ${workspaceFiles.projectId} IS NOT NULL THEN 'project' ELSE 'workspace' END`, + sql`coalesce(${workspaceFiles.projectId}, ${workspaceFiles.workspaceId})`, asc(workspaceFiles.id) ) .limit(FILE_SEARCH_BACKFILL_PAGE_SIZE) @@ -299,8 +299,8 @@ async function reapStaleClaims( workspaceFiles, and( eq(workspaceFiles.id, workspaceFileSearchRevision.fileId), - sql`coalesce(${workspaceFiles.entityType}, 'workspace') = coalesce(${workspaceFileSearchRevision.entityType}, 'workspace')`, - sql`coalesce(${workspaceFiles.entityId}, ${workspaceFiles.workspaceId}) = coalesce(${workspaceFileSearchRevision.entityId}, ${workspaceFileSearchRevision.workspaceId})`, + sql`CASE WHEN ${workspaceFiles.projectId} IS NOT NULL THEN 'project' ELSE 'workspace' END = coalesce(${workspaceFileSearchRevision.entityType}, 'workspace')`, + sql`coalesce(${workspaceFiles.projectId}, ${workspaceFiles.workspaceId}) = coalesce(${workspaceFileSearchRevision.entityId}, ${workspaceFileSearchRevision.workspaceId})`, sql`${workspaceFiles.context} = coalesce(${workspaceFileSearchRevision.entityType}, 'workspace')`, isNull(workspaceFiles.deletedAt), eq(workspaceFiles.contentUpdatedAt, workspaceFileSearchRevision.sourceContentUpdatedAt) @@ -432,8 +432,8 @@ async function claimQueuedOwnerJobs( CROSS JOIN LATERAL ( SELECT 1 FROM workspace_files file WHERE file.id = search_index.file_id AND file.context = selected.entity_type - AND coalesce(file.entity_type, 'workspace') = selected.entity_type - AND coalesce(file.entity_id, file.workspace_id) = selected.entity_id + AND CASE WHEN file.project_id IS NOT NULL THEN 'project' ELSE 'workspace' END = selected.entity_type + AND coalesce(file.project_id, file.workspace_id) = selected.entity_id AND file.deleted_at IS NULL AND file.content_updated_at = search_index.source_content_updated_at LIMIT 1 ) live_file diff --git a/apps/sim/lib/workspace-files/search/owners.integration.ts b/apps/sim/lib/workspace-files/search/owners.integration.ts index 59d811c5dbc..52d9fd689b2 100644 --- a/apps/sim/lib/workspace-files/search/owners.integration.ts +++ b/apps/sim/lib/workspace-files/search/owners.integration.ts @@ -153,8 +153,7 @@ async function create( .values({ id, userId: f.ownerId, - entityType: 'project', - entityId: f.projectId, + projectId: f.projectId, context: 'project', key, originalName: name, @@ -185,7 +184,7 @@ async function claim(fileId: string) { async function changeFile(fileId: string, content: string) { const [old] = await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, fileId)) if (!old) throw new Error('Search source fixture missing') - const key = `project/${old.entityId}/${generateId()}/${old.originalName}` + const key = `project/${old.projectId}/${generateId()}/${old.originalName}` await mkdir(dirname(join(storageRoot, key)), { recursive: true }) await writeFile(join(storageRoot, key), content) await db @@ -208,8 +207,7 @@ async function createFolder( .values({ id: generateId(), resourceType: 'file', - entityType: 'project', - entityId: f.projectId, + projectId: f.projectId, userId: f.ownerId, name, parentId, @@ -786,7 +784,7 @@ afterAll(async () => { .from(workspaceFiles) .where( inArray( - workspaceFiles.entityId, + workspaceFiles.projectId, fixtures.flatMap((f) => [f.projectId, f.workspaceId]) ) ) @@ -813,7 +811,7 @@ afterAll(async () => { } } for (const f of fixtures) { - await db.delete(folder).where(eq(folder.entityId, f.projectId)) + await db.delete(folder).where(eq(folder.projectId, f.projectId)) await deleteWorkspaceFixture(db, eq(workspace.id, f.workspaceId)) await db.delete(project).where(eq(project.id, f.projectId)) await db.delete(organization).where(eq(organization.id, f.organizationId)) diff --git a/apps/sim/lib/workspace-files/search/scope.ts b/apps/sim/lib/workspace-files/search/scope.ts index cdda9caf7e3..20099d2866f 100644 --- a/apps/sim/lib/workspace-files/search/scope.ts +++ b/apps/sim/lib/workspace-files/search/scope.ts @@ -67,8 +67,8 @@ export function currentFileSearchDependencies(buildId: SQLWrapper, owner: Editab SELECT 1 FROM file_search_dependency dependency LEFT JOIN workspace_files input ON input.id = dependency.file_id AND input.context = ${owner.entityType} - AND coalesce(input.entity_type, 'workspace') = ${owner.entityType} - AND coalesce(input.entity_id, input.workspace_id) = ${owner.entityId} + AND (CASE WHEN input.project_id IS NOT NULL THEN 'project' ELSE 'workspace' END) = ${owner.entityType} + AND coalesce(input.project_id, input.workspace_id) = ${owner.entityId} WHERE dependency.build_id = ${buildId} AND (input.id IS NULL OR input.deleted_at IS NOT NULL OR input.key <> dependency.key OR input.content_updated_at <> dependency.source_content_updated_at) diff --git a/packages/db/file-creator-lifetime.integration.ts b/packages/db/file-creator-lifetime.integration.ts index 75c3467a67f..0810b034a39 100644 --- a/packages/db/file-creator-lifetime.integration.ts +++ b/packages/db/file-creator-lifetime.integration.ts @@ -129,20 +129,20 @@ describe('Project creator lifetime in PostgreSQL', () => { check( 'creator deletion preserves Project folders, heads, history and immutable attribution', async () => { - await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES ('folder', 'Docs', 'user-a', 'file', 'project', 'project-a')` - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) - VALUES ('file', 'project', 'user-a', 'project', 'project-a', 'folder')` + await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) + VALUES ('folder', 'Docs', 'user-a', 'file', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, project_id, folder_id) + VALUES ('file', 'project', 'user-a', 'project-a', 'folder')` await sql`INSERT INTO workspace_file_version (id, file_id, author_user_ids) VALUES ('version', 'file', ARRAY['user-a'])` const [before] = await sql`SELECT key, content_updated_at, secret_provenance_version FROM workspace_files` await sql`DELETE FROM "user" WHERE id = 'user-a'` expect( - await sql`SELECT user_id, original_creator_user_id, entity_id FROM workspace_files` - ).toEqual([{ user_id: null, original_creator_user_id: 'user-a', entity_id: 'project-a' }]) - expect(await sql`SELECT user_id, original_creator_user_id, entity_id FROM folder`).toEqual([ - { user_id: null, original_creator_user_id: 'user-a', entity_id: 'project-a' }, + await sql`SELECT user_id, original_creator_user_id, project_id FROM workspace_files` + ).toEqual([{ user_id: null, original_creator_user_id: 'user-a', project_id: 'project-a' }]) + expect(await sql`SELECT user_id, original_creator_user_id, project_id FROM folder`).toEqual([ + { user_id: null, original_creator_user_id: 'user-a', project_id: 'project-a' }, ]) expect( ( @@ -178,11 +178,11 @@ describe('Project creator lifetime in PostgreSQL', () => { await sql`ALTER TABLE workspace_files DISABLE TRIGGER workspace_files_creator_lifetime` await sql`ALTER TABLE folder DISABLE TRIGGER folder_creator_lifetime` try { - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, original_name) - SELECT 'file-' || lpad(id::text, 4, '0'), 'project', 'user-a', 'project', 'project-a', 'document-' || id + await sql`INSERT INTO workspace_files (id, context, user_id, project_id, original_name) + SELECT 'file-' || lpad(id::text, 4, '0'), 'project', 'user-a', 'project-a', 'document-' || id FROM generate_series(1, 507) id` - await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - SELECT 'folder-' || lpad(id::text, 4, '0'), 'Docs ' || id, 'user-a', 'file', 'project', 'project-a' + await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) + SELECT 'folder-' || lpad(id::text, 4, '0'), 'Docs ' || id, 'user-a', 'file', 'project-a' FROM generate_series(1, 507) id` } finally { await sql`ALTER TABLE workspace_files ENABLE TRIGGER workspace_files_creator_lifetime` @@ -220,16 +220,16 @@ describe('Project creator lifetime in PostgreSQL', () => { check( 'rejects missing and forged Project creators and immutable snapshot replacement', async () => { - await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, original_creator_user_id) - VALUES ('missing', 'project', NULL, 'project', 'project-a', 'user-a')`).rejects.toMatchObject( - { code: '23514' } - ) - await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, original_creator_user_id) - VALUES ('forged', 'project', 'user-a', 'project', 'project-a', 'user-b')`).rejects.toMatchObject( - { code: '23514' } - ) - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + await expect(sql`INSERT INTO workspace_files (id, context, user_id, project_id, original_creator_user_id) + VALUES ('missing', 'project', NULL, 'project-a', 'user-a')`).rejects.toMatchObject({ + code: '23514', + }) + await expect(sql`INSERT INTO workspace_files (id, context, user_id, project_id, original_creator_user_id) + VALUES ('forged', 'project', 'user-a', 'project-a', 'user-b')`).rejects.toMatchObject({ + code: '23514', + }) + await sql`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('file', 'project', 'user-a', 'project-a')` for (const creator of ['user-b', null]) { await expect( sql`UPDATE workspace_files SET original_creator_user_id = ${creator} WHERE id = 'file'` @@ -238,10 +238,10 @@ describe('Project creator lifetime in PostgreSQL', () => { await expect( sql`UPDATE workspace_files SET user_id = 'user-b' WHERE id = 'file'` ).rejects.toMatchObject({ code: '23514' }) - await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id, original_creator_user_id) - VALUES ('forged', 'Docs', 'user-a', 'file', 'project', 'project-a', 'user-b')`).rejects.toMatchObject( - { code: '23514' } - ) + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, project_id, original_creator_user_id) + VALUES ('forged', 'Docs', 'user-a', 'file', 'project-a', 'user-b')`).rejects.toMatchObject({ + code: '23514', + }) await expect(sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) VALUES ('legacy-null', 'workspace', NULL, 'workspace-a')`).rejects.toMatchObject({ code: '23514', @@ -253,8 +253,8 @@ describe('Project creator lifetime in PostgreSQL', () => { 'retained Project owner deletion stays fail-closed and rolls back creator cleanup', async () => { await sql`UPDATE project SET owner_id = 'user-a' WHERE id = 'project-a'` - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('file', 'project', 'user-a', 'project-a')` await expect(sql`DELETE FROM "user" WHERE id = 'user-a'`).rejects.toMatchObject({ code: '23503', }) @@ -289,8 +289,8 @@ describe('Project creator lifetime in PostgreSQL', () => { ) await snapshot.promise const insertion = sql.begin(async (tx) => { - await tx`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + await tx`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('file', 'project', 'user-a', 'project-a')` inserted.resolve() await releaseInsert.promise }) @@ -329,8 +329,8 @@ describe('Project creator lifetime in PostgreSQL', () => { const insertion = sql.begin(async (tx) => { const [connection] = await tx`SELECT pg_backend_pid() AS pid` insertPid.resolve(connection.pid) - await tx`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + await tx`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('file', 'project', 'user-a', 'project-a')` }) const insertionOutcome = insertion.then( () => ({ code: null }), diff --git a/packages/db/file-entity-ownership.integration.ts b/packages/db/file-entity-ownership.integration.ts index be7339f03ae..ad11cf503ad 100644 --- a/packages/db/file-entity-ownership.integration.ts +++ b/packages/db/file-entity-ownership.integration.ts @@ -1,5 +1,4 @@ import { readFileSync } from 'node:fs' -import { backfillWorkspaceFileEntities } from '@sim/db/script-migrations/0030_backfill_workspace_file_entities' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { createDeferred } from '@sim/testing/helpers/deferred' import { sleep } from '@sim/utils/helpers' @@ -88,64 +87,30 @@ describe('file entity ownership migration in PostgreSQL', () => { } }) - it('backfills several bounded pages, preserves revisions, and reports unresolved legacy rows', async () => { - await sql`ALTER TABLE workspace_files DISABLE TRIGGER workspace_files_sync_entity_binding` - try { - await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) - SELECT 'file-' || lpad(id::text, 4, '0'), 'workspace', 'user-a', 'workspace-a' - FROM generate_series(1, 506) id` - await sql`INSERT INTO workspace_files (id, context, user_id, organization_id) - VALUES ('org-kb', 'knowledge-base', 'user-a', 'organization-a')` - await sql`INSERT INTO workspace_files (id, context, user_id) - VALUES ('personal', 'copilot', 'user-a'), ('orphan-kb', 'knowledge-base', 'user-a'), - ('unknown', 'chat', 'user-a')` - } finally { - await sql`ALTER TABLE workspace_files ENABLE TRIGGER workspace_files_sync_entity_binding` - } - - const result = await backfillWorkspaceFileEntities(sql) - expect(result.backfilled).toBe(508) - expect(result.unresolved).toEqual([ - { context: 'knowledge-base', count: 1, sampleFileIds: ['orphan-kb'] }, - { context: 'chat', count: 1, sampleFileIds: ['unknown'] }, - ]) - const [changed] = await sql<{ count: number }[]>` - SELECT count(*)::integer AS count FROM workspace_files - WHERE key <> 'unchanged-object-key' - OR content_updated_at <> TIMESTAMP '2026-10-03 01:02:03.456' - OR secret_provenance_version <> 1 - ` - expect(changed.count).toBe(0) - const owners = await sql`SELECT id, entity_type, entity_id FROM workspace_files - WHERE id IN ('file-0001', 'org-kb', 'personal', 'orphan-kb') ORDER BY id` - expect(owners).toEqual([ - { id: 'file-0001', entity_type: 'workspace', entity_id: 'workspace-a' }, - { id: 'org-kb', entity_type: 'organization', entity_id: 'organization-a' }, - { id: 'orphan-kb', entity_type: null, entity_id: null }, - { id: 'personal', entity_type: 'user', entity_id: 'user-a' }, - ]) - expect((await backfillWorkspaceFileEntities(sql)).backfilled).toBe(0) - }) - it('keeps legacy inserts and owner changes consistent while retaining chat purpose bindings', async () => { await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, chat_id) VALUES ('chat-file', 'mothership', 'user-a', 'workspace-a', 'chat-a')` await sql`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'chat-file'` - const [chat] = await sql`SELECT entity_type, entity_id, chat_id FROM workspace_files` - expect(chat).toEqual({ entity_type: 'workspace', entity_id: 'workspace-b', chat_id: 'chat-a' }) + const [chat] = await sql`SELECT workspace_id, project_id, chat_id FROM workspace_files` + expect(chat).toEqual({ workspace_id: 'workspace-b', project_id: null, chat_id: 'chat-a' }) await sql`UPDATE workspace_files SET context = 'workspace', chat_id = NULL WHERE id = 'chat-file'` await sql`UPDATE workspace_files SET user_id = 'user-b' WHERE id = 'chat-file'` - const [materialized] = await sql`SELECT entity_type, entity_id, chat_id FROM workspace_files` + const [materialized] = await sql`SELECT workspace_id, project_id, chat_id FROM workspace_files` expect(materialized).toEqual({ - entity_type: 'workspace', - entity_id: 'workspace-b', + workspace_id: 'workspace-b', + project_id: null, chat_id: null, }) await sql`INSERT INTO workspace_files (id, context, user_id) VALUES ('personal', 'copilot', 'user-a')` await sql`UPDATE workspace_files SET user_id = 'user-b' WHERE id = 'personal'` const [personal] = - await sql`SELECT entity_type, entity_id FROM workspace_files WHERE id = 'personal'` - expect(personal).toEqual({ entity_type: 'user', entity_id: 'user-b' }) + await sql`SELECT user_id, workspace_id, project_id, organization_id FROM workspace_files WHERE id = 'personal'` + expect(personal).toEqual({ + user_id: 'user-b', + workspace_id: null, + project_id: null, + organization_id: null, + }) }) it.each(['execution', 'workspace-logos', 'knowledge-base'])( @@ -153,62 +118,90 @@ describe('file entity ownership migration in PostgreSQL', () => { async (context) => { await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) VALUES ('file', ${context}, 'user-a', 'workspace-a')` - const [row] = await sql`SELECT entity_type, entity_id FROM workspace_files WHERE id = 'file'` - expect(row).toEqual({ entity_type: 'workspace', entity_id: 'workspace-a' }) + const [row] = + await sql`SELECT workspace_id, project_id FROM workspace_files WHERE id = 'file'` + expect(row).toEqual({ workspace_id: 'workspace-a', project_id: null }) } ) - it('refuses contradictory, incomplete, and unregistered explicit ownership without changing stored ownership', async () => { - await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) - VALUES ('file', 'workspace', 'user-a', 'workspace-a')` - for (const pair of [ - { type: 'workspace', id: 'workspace-b' }, - { type: 'workspace', id: null }, - { type: null, id: 'workspace-a' }, - { type: 'user', id: 'user-a' }, - { type: 'future', id: 'workspace-a' }, - { type: 'workspace', id: '' }, - ]) { - await expect(sql`UPDATE workspace_files SET entity_type = ${pair.type}, entity_id = ${pair.id} - WHERE id = 'file'`).rejects.toMatchObject({ code: '23514' }) + it.each([ + ['workspace-a', 'project-a', null], + ['workspace-a', null, 'organization-a'], + [null, 'project-a', 'organization-a'], + ['workspace-a', 'project-a', 'organization-a'], + ])( + 'rejects competing workspace %s, project %s, and organization %s owners', + async (workspaceId, projectId, organizationId) => { + const context = projectId ? 'project' : 'knowledge-base' + await expect(sql`INSERT INTO workspace_files + (id, context, user_id, workspace_id, project_id, organization_id, deleted_at) + VALUES ('mixed', ${context}, 'user-a', ${workspaceId}, ${projectId}, ${organizationId}, now())`).rejects.toMatchObject( + { code: '23514', constraint_name: 'workspace_files_owner_check' } + ) + await sql`INSERT INTO workspace_files (id, context, user_id) VALUES ('file', 'copilot', 'user-a')` + await expect(sql`UPDATE workspace_files SET context = ${context}, workspace_id = ${workspaceId}, + project_id = ${projectId}, organization_id = ${organizationId} WHERE id = 'file'`).rejects.toMatchObject( + { code: '23514', constraint_name: 'workspace_files_owner_check' } + ) + expect( + await sql`SELECT workspace_id, project_id, organization_id FROM workspace_files` + ).toEqual([{ workspace_id: null, project_id: null, organization_id: null }]) } - const [row] = await sql`SELECT entity_type, entity_id FROM workspace_files WHERE id = 'file'` - expect(row).toEqual({ entity_type: 'workspace', entity_id: 'workspace-a' }) - await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('unknown', 'general', 'user-a', 'user', 'user-a')`).rejects.toMatchObject({ + ) + + it('requires a real Project and rejects absent, incorrect-purpose, or chat-bound Project ownership', async () => { + await expect(sql`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('missing', 'project', 'user-a', 'missing')`).rejects.toMatchObject({ code: '23503' }) + await expect(sql`INSERT INTO workspace_files (id, context, user_id) + VALUES ('unowned', 'project', 'user-a')`).rejects.toMatchObject({ code: '23514' }) + await expect(sql`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('purpose', 'workspace', 'user-a', 'project-a')`).rejects.toMatchObject({ + code: '23514', + }) + await expect(sql`INSERT INTO workspace_files (id, context, user_id, project_id, chat_id) + VALUES ('chat', 'project', 'user-a', 'project-a', 'chat-a')`).rejects.toMatchObject({ code: '23514', }) }) - it('requires a real Project and refuses legacy tenancy or unsupported folders on a Project file', async () => { - await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('missing', 'project', 'user-a', 'project', 'missing')`).rejects.toMatchObject({ - code: '23503', + it('allows independent concurrent file inserts into the same Project', async () => { + const inserted = createDeferred() + const release = createDeferred() + const first = sql.begin(async (tx) => { + await tx`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('first', 'project', 'user-a', 'project-a')` + inserted.resolve() + await release.promise }) - await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, workspace_id) - VALUES ('bound', 'project', 'user-a', 'project', 'project-a', 'workspace-a')`).rejects.toMatchObject( - { code: '23514' } - ) - await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) - VALUES ('folder', 'project', 'user-a', 'project', 'project-a', 'workspace-folder')`).rejects.toMatchObject( - { code: '23514' } - ) - await expect(sql`INSERT INTO workspace_files (id, context, user_id) - VALUES ('unowned', 'project', 'user-a')`).rejects.toMatchObject({ code: '23514' }) + try { + await inserted.promise + await sql.begin(async (tx) => { + await tx`SET LOCAL statement_timeout = '2s'` + await tx`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('second', 'project', 'user-b', 'project-a')` + }) + } finally { + release.resolve() + await first + } + expect(await sql`SELECT id FROM workspace_files ORDER BY id`).toEqual([ + { id: 'first' }, + { id: 'second' }, + ]) }) it('retains Project ownership through attribution changes and blocks owner deletion while retained files exist', async () => { const [projectBefore] = await sql`SELECT * FROM project WHERE id = 'project-a'` - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('file', 'project', 'user-a', 'project-a')` const [projectAfter] = await sql`SELECT * FROM project WHERE id = 'project-a'` expect(projectAfter).toEqual(projectBefore) await sql`UPDATE workspace_files SET user_id = 'user-b', deleted_at = now() WHERE id = 'file'` await expect( - sql`UPDATE workspace_files SET entity_type = NULL, entity_id = NULL WHERE id = 'file'` + sql`UPDATE workspace_files SET project_id = NULL WHERE id = 'file'` ).rejects.toMatchObject({ code: '23514' }) await expect( - sql`UPDATE workspace_files SET entity_id = 'project-b' WHERE id = 'file'` + sql`UPDATE workspace_files SET project_id = 'project-b' WHERE id = 'file'` ).rejects.toMatchObject({ code: '23514' }) await expect(sql`DELETE FROM project WHERE id = 'project-a'`).rejects.toMatchObject({ code: '23503', @@ -222,8 +215,8 @@ describe('file entity ownership migration in PostgreSQL', () => { }) it('protects shared Project files from uploader cascade until attribution has been reassigned', async () => { - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('shared-file', 'project', 'user-a', 'project', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('shared-file', 'project', 'user-a', 'project-a')` await expect(sql`DELETE FROM "user" WHERE id = 'user-a'`).rejects.toMatchObject({ code: '23503', }) @@ -231,8 +224,8 @@ describe('file entity ownership migration in PostgreSQL', () => { await sql`UPDATE workspace_files SET user_id = 'user-b' WHERE id = 'shared-file'` await sql`DELETE FROM "user" WHERE id = 'user-a'` const [file] = - await sql`SELECT user_id, entity_type, entity_id FROM workspace_files WHERE id = 'shared-file'` - expect(file).toEqual({ user_id: 'user-b', entity_type: 'project', entity_id: 'project-a' }) + await sql`SELECT user_id, project_id FROM workspace_files WHERE id = 'shared-file'` + expect(file).toEqual({ user_id: 'user-b', project_id: 'project-a' }) }) it('preserves legacy workspace, organization, and personal owner deletion behavior', async () => { @@ -256,8 +249,8 @@ describe('file entity ownership migration in PostgreSQL', () => { const inserted = createDeferred() const release = createDeferred() const creation = sql.begin(async (tx) => { - await tx`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + await tx`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('file', 'project', 'user-a', 'project-a')` inserted.resolve() await release.promise }) @@ -272,7 +265,7 @@ describe('file entity ownership migration in PostgreSQL', () => { await tx`DELETE FROM project WHERE id = 'project-a'` }) rejection = expect(deletion).rejects.toMatchObject({ - code: isolation === 'repeatable read' ? '40001' : '23503', + code: '23503', }) await waitForDatabaseLock(await deleting.promise) } finally { @@ -290,8 +283,8 @@ describe('file entity ownership migration in PostgreSQL', () => { const inserted = createDeferred() const release = createDeferred() const creation = sql.begin(async (tx) => { - await tx`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + await tx`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('file', 'project', 'user-a', 'project-a')` inserted.resolve() await release.promise }) @@ -336,8 +329,8 @@ describe('file entity ownership migration in PostgreSQL', () => { await tx`SET LOCAL statement_timeout = '5s'` const [backend] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` inserting.resolve(backend.pid) - await tx`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('file', 'project', 'user-a', 'project', 'project-a')` + await tx`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('file', 'project', 'user-a', 'project-a')` }) rejection = expect(creation).rejects.toMatchObject({ code: isolation === 'repeatable read' ? '40001' : '23503', @@ -356,7 +349,7 @@ describe('file entity ownership migration in PostgreSQL', () => { await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) VALUES ('file', 'workspace', 'user-a', 'workspace-a')` await applyMigration() - const [row] = await sql`SELECT id, entity_type, entity_id FROM workspace_files` - expect(row).toEqual({ id: 'file', entity_type: 'workspace', entity_id: 'workspace-a' }) + const [row] = await sql`SELECT id, workspace_id, project_id FROM workspace_files` + expect(row).toEqual({ id: 'file', workspace_id: 'workspace-a', project_id: null }) }) }) diff --git a/packages/db/file-folder-version-ownership.integration.ts b/packages/db/file-folder-version-ownership.integration.ts index 5394cf556aa..dbd7e428665 100644 --- a/packages/db/file-folder-version-ownership.integration.ts +++ b/packages/db/file-folder-version-ownership.integration.ts @@ -1,8 +1,5 @@ import { readFileSync } from 'node:fs' -import { - backfillFileFolderEntities, - backfillFileFolderEntitiesMigration, -} from '@sim/db/script-migrations/0031_backfill_file_folder_entities' +import { validateFileOwnershipMigration } from '@sim/db/script-migrations/0031_validate_file_ownership' import { runScriptMigrations } from '@sim/db/script-migrations/index' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { createDeferred } from '@sim/testing/helpers/deferred' @@ -107,59 +104,16 @@ describe('file folder and version ownership in PostgreSQL', () => { } }) - it('backfills several bounded pages without changing folder hierarchy or version attribution', async () => { - await sql`ALTER TABLE folder DISABLE TRIGGER folder_entity_binding` - await sql`ALTER TABLE workspace_file_version DISABLE TRIGGER workspace_file_version_owner_match` - try { - await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) - SELECT 'folder-' || lpad(id::text, 4, '0'), 'Folder ' || id, 'user-a', 'workspace-a', 'file' - FROM generate_series(1, 506) id` - await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, parent_id) - VALUES ('child', 'Child', 'user-a', 'workspace-a', 'file', 'folder-0001')` - await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) - VALUES ('file', 'workspace', 'user-a', 'workspace-a')` - await sql`INSERT INTO workspace_file_version (id, file_id, workspace_id) - VALUES ('bad-version', 'file', 'workspace-b')` - } finally { - await sql`ALTER TABLE folder ENABLE TRIGGER folder_entity_binding` - await sql`ALTER TABLE workspace_file_version ENABLE TRIGGER workspace_file_version_owner_match` - } - await sql`UPDATE folder SET name = 'Renamed' WHERE id = 'child'` - expect(await backfillFileFolderEntities(sql)).toEqual({ - backfilled: 507, - mismatchedVersions: 1, - sampleVersionIds: ['bad-version'], - }) - expect((await backfillFileFolderEntities(sql)).backfilled).toBe(0) - const [child] = - await sql`SELECT name, parent_id, entity_type, entity_id FROM folder WHERE id = 'child'` - expect(child).toEqual({ - name: 'Renamed', - parent_id: 'folder-0001', - entity_type: 'workspace', - entity_id: 'workspace-a', - }) - const [version] = - await sql`SELECT workspace_id, key FROM workspace_file_version WHERE id = 'bad-version'` - expect(version).toEqual({ workspace_id: 'workspace-b', key: 'unchanged-version-key' }) - }) - - it('preserves metadata and content changes on unbackfilled files with retained versions', async () => { - await sql`ALTER TABLE workspace_files DISABLE TRIGGER workspace_files_sync_entity_binding` - try { - await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) - VALUES ('file', 'workspace', 'user-a', 'workspace-a')` - } finally { - await sql`ALTER TABLE workspace_files ENABLE TRIGGER workspace_files_sync_entity_binding` - } + it('preserves metadata and content changes on existing workspace files with retained versions', async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a')` await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` await sql`UPDATE workspace_files SET key = 'new-revision-key', original_name = 'renamed.md' WHERE id = 'file'` - await sql`UPDATE workspace_files SET entity_type = entity_type WHERE id = 'file'` const [file] = - await sql`SELECT entity_type, entity_id, key, secret_provenance_version FROM workspace_files` + await sql`SELECT workspace_id, project_id, key, secret_provenance_version FROM workspace_files` expect(file).toEqual({ - entity_type: 'workspace', - entity_id: 'workspace-a', + workspace_id: 'workspace-a', + project_id: null, key: 'new-revision-key', secret_provenance_version: 1, }) @@ -176,7 +130,7 @@ describe('file folder and version ownership in PostgreSQL', () => { await sql`ALTER TABLE workspace_file_version ENABLE TRIGGER workspace_file_version_owner_match` } - await expect(runScriptMigrations(sql, [backfillFileFolderEntitiesMigration])).rejects.toThrow( + await expect(runScriptMigrations(sql, [validateFileOwnershipMigration])).rejects.toThrow( 'Retained file versions require ownership repair' ) expect(await sql`SELECT name FROM script_migrations`).toEqual([]) @@ -186,10 +140,10 @@ describe('file folder and version ownership in PostgreSQL', () => { await sql`UPDATE workspace_file_version SET workspace_id = 'workspace-a' WHERE id = 'conflicting-version'` - await runScriptMigrations(sql, [backfillFileFolderEntitiesMigration]) - await runScriptMigrations(sql, [backfillFileFolderEntitiesMigration]) + await runScriptMigrations(sql, [validateFileOwnershipMigration]) + await runScriptMigrations(sql, [validateFileOwnershipMigration]) expect(await sql`SELECT name FROM script_migrations`).toEqual([ - { name: '0031_backfill_file_folder_entities' }, + { name: '0031_validate_file_ownership' }, ]) expect( await sql`SELECT workspace_id, key FROM workspace_file_version WHERE id = 'conflicting-version'` @@ -198,34 +152,26 @@ describe('file folder and version ownership in PostgreSQL', () => { it('requires explicit Project file-folder ownership and rejects unknown or conflicting bindings', async () => { const [before] = await sql`SELECT * FROM project WHERE id = 'project-a'` - await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES ('project-folder', 'Documentation', 'user-a', 'file', 'project', 'project-a')` + await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) + VALUES ('project-folder', 'Documentation', 'user-a', 'file', 'project-a')` const [after] = await sql`SELECT * FROM project WHERE id = 'project-a'` expect(after).toEqual(before) for (const resourceType of ['workflow', 'knowledge_base', 'table']) { - await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES (${resourceType}, 'Invalid', 'user-a', ${resourceType}, 'project', 'project-a')`).rejects.toMatchObject( + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) + VALUES (${resourceType}, 'Invalid', 'user-a', ${resourceType}, 'project-a')`).rejects.toMatchObject( { code: '23514' } ) } - for (const pair of [ - { type: null, id: null }, - { type: 'organization', id: 'organization-a' }, - { type: 'project', id: null }, - ]) { - await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES ('invalid', 'Invalid', 'user-a', 'file', ${pair.type}, ${pair.id})`).rejects.toMatchObject( - { code: '23514' } - ) - } - await expect(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, entity_type, entity_id) - VALUES ('conflict', 'Invalid', 'user-a', 'workspace-a', 'file', 'project', 'project-a')`).rejects.toMatchObject( + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type) + VALUES ('invalid', 'Invalid', 'user-a', 'file')`).rejects.toMatchObject({ code: '23514' }) + await expect(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, project_id) + VALUES ('conflict', 'Invalid', 'user-a', 'workspace-a', 'file', 'project-a')`).rejects.toMatchObject( { code: '23514' } ) - await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES ('missing', 'Invalid', 'user-a', 'file', 'project', 'missing')`).rejects.toMatchObject( - { code: '23503' } - ) + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) + VALUES ('missing', 'Invalid', 'user-a', 'file', 'missing')`).rejects.toMatchObject({ + code: '23503', + }) }) it('keeps folder identity and type immutable and rejects cross-owner parents', async () => { @@ -240,7 +186,7 @@ describe('file folder and version ownership in PostgreSQL', () => { sql`UPDATE folder SET parent_id = 'workflow-folder' WHERE id = 'file-folder'` ).rejects.toMatchObject({ code: '23514' }) await expect( - sql`UPDATE folder SET workspace_id = 'workspace-b', entity_id = 'workspace-b' WHERE id = 'file-folder'` + sql`UPDATE folder SET workspace_id = 'workspace-b' WHERE id = 'file-folder'` ).rejects.toMatchObject({ code: '23514' }) await expect( sql`UPDATE folder SET resource_type = 'table' WHERE id = 'file-folder'` @@ -274,42 +220,42 @@ describe('file folder and version ownership in PostgreSQL', () => { it('separates active names by owner type and parent and rejects conflicting restoration', async () => { await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) VALUES ('workspace-folder', 'Docs', 'user-a', 'same-id', 'file')` - await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES ('project-folder', 'Docs', 'user-a', 'file', 'project', 'same-id')` + await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) + VALUES ('project-folder', 'Docs', 'user-a', 'file', 'same-id')` await expect( sql`UPDATE folder SET parent_id = 'workspace-folder' WHERE id = 'project-folder'` ).rejects.toMatchObject({ code: '23514' }) - await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES ('duplicate', 'Docs', 'user-a', 'file', 'project', 'same-id')`).rejects.toMatchObject({ + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) + VALUES ('duplicate', 'Docs', 'user-a', 'file', 'same-id')`).rejects.toMatchObject({ code: '23505', }) - await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id, parent_id) - VALUES ('nested', 'Docs', 'user-a', 'file', 'project', 'same-id', 'project-folder')` - await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id, deleted_at) - VALUES ('deleted', 'Docs', 'user-a', 'file', 'project', 'same-id', now())` + await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id, parent_id) + VALUES ('nested', 'Docs', 'user-a', 'file', 'same-id', 'project-folder')` + await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id, deleted_at) + VALUES ('deleted', 'Docs', 'user-a', 'file', 'same-id', now())` await expect( sql`UPDATE folder SET deleted_at = NULL WHERE id = 'deleted'` ).rejects.toMatchObject({ code: '23505' }) - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('project-file', 'project', 'user-a', 'project', 'same-id')` + await sql`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('project-file', 'project', 'user-a', 'same-id')` await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) VALUES ('workspace-file', 'workspace', 'user-a', 'same-id')` - await expect(sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('duplicate-file', 'project', 'user-a', 'project', 'same-id')`).rejects.toMatchObject({ + await expect(sql`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('duplicate-file', 'project', 'user-a', 'same-id')`).rejects.toMatchObject({ code: '23505', }) - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) - VALUES ('nested-file', 'project', 'user-a', 'project', 'same-id', 'project-folder')` + await sql`INSERT INTO workspace_files (id, context, user_id, project_id, folder_id) + VALUES ('nested-file', 'project', 'user-a', 'same-id', 'project-folder')` }) it('accepts Project file folders and rejects mismatched owners and resource types', async () => { await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) VALUES ('workspace-folder', 'Files', 'user-a', 'workspace-a', 'file'), ('workflow-folder', 'Workflows', 'user-a', 'workspace-a', 'workflow')` - await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES ('project-folder', 'Docs', 'user-a', 'file', 'project', 'project-a')` - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) - VALUES ('project-file', 'project', 'user-a', 'project', 'project-a', 'project-folder')` + await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) + VALUES ('project-folder', 'Docs', 'user-a', 'file', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, project_id, folder_id) + VALUES ('project-file', 'project', 'user-a', 'project-a', 'project-folder')` await expect( sql`UPDATE workspace_files SET folder_id = 'workspace-folder' WHERE id = 'project-file'` ).rejects.toMatchObject({ code: '23514' }) @@ -324,8 +270,8 @@ describe('file folder and version ownership in PostgreSQL', () => { }) it('protects retained Project folders from parent and creator deletion', async () => { - await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id, deleted_at) - VALUES ('project-folder', 'Docs', 'user-a', 'file', 'project', 'project-a', now())` + await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id, deleted_at) + VALUES ('project-folder', 'Docs', 'user-a', 'file', 'project-a', now())` await expect(sql`DELETE FROM project WHERE id = 'project-a'`).rejects.toMatchObject({ code: '23503', }) @@ -343,8 +289,8 @@ describe('file folder and version ownership in PostgreSQL', () => { await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) VALUES ('workspace-file', 'workspace', 'user-a', 'workspace-a'), ('chat-file', 'mothership', 'user-a', 'workspace-a')` - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id) - VALUES ('project-file', 'project', 'user-a', 'project', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('project-file', 'project', 'user-a', 'project-a')` await sql`INSERT INTO workspace_files (id, context, user_id) VALUES ('personal', 'copilot', 'user-a')` await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('workspace-version', 'workspace-file'), ('project-version', 'project-file')` @@ -387,10 +333,11 @@ describe('file folder and version ownership in PostgreSQL', () => { async (ownerType, isolation) => { const ownerId = ownerType === 'workspace' ? 'workspace-a' : 'project-a' const workspaceId = ownerType === 'workspace' ? ownerId : null + const projectId = ownerType === 'project' ? ownerId : null const lockKey = `resource_folders:file:${ownerType === 'workspace' ? ownerId : `${ownerType}:${ownerId}`}` - await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, entity_type, entity_id) - VALUES ('folder-a', 'A', 'user-a', ${workspaceId}, 'file', ${ownerType}, ${ownerId}), - ('folder-b', 'B', 'user-a', ${workspaceId}, 'file', ${ownerType}, ${ownerId})` + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, project_id) + VALUES ('folder-a', 'A', 'user-a', ${workspaceId}, 'file', ${projectId}), + ('folder-b', 'B', 'user-a', ${workspaceId}, 'file', ${projectId})` const moved = createDeferred() const release = createDeferred() const firstMove = sql.begin(async (tx) => { @@ -458,8 +405,8 @@ describe('file folder and version ownership in PostgreSQL', () => { await creation } await rejection - const [file] = await sql`SELECT entity_id FROM workspace_files WHERE id = 'file'` - expect(file.entity_id).toBe('workspace-a') + const [file] = await sql`SELECT workspace_id FROM workspace_files WHERE id = 'file'` + expect(file.workspace_id).toBe('workspace-a') } ) @@ -505,8 +452,8 @@ describe('file folder and version ownership in PostgreSQL', () => { const inserted = createDeferred() const release = createDeferred() const creation = sql.begin(async (tx) => { - await tx`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES ('folder', 'Docs', 'user-a', 'file', 'project', 'project-a')` + await tx`INSERT INTO folder (id, name, user_id, resource_type, project_id) + VALUES ('folder', 'Docs', 'user-a', 'file', 'project-a')` inserted.resolve() await release.promise }) @@ -521,7 +468,7 @@ describe('file folder and version ownership in PostgreSQL', () => { await tx`DELETE FROM project WHERE id = 'project-a'` }) rejection = expect(deletion).rejects.toMatchObject({ - code: isolation === 'repeatable read' ? '40001' : '23503', + code: '23503', }) await waitForDatabaseLock(await waiting.promise) } finally { @@ -534,10 +481,10 @@ describe('file folder and version ownership in PostgreSQL', () => { ) it('replays the expansion while preserving existing Project folders and version pointers', async () => { - await sql`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES ('folder', 'Docs', 'user-a', 'file', 'project', 'project-a')` - await sql`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) - VALUES ('file', 'project', 'user-a', 'project', 'project-a', 'folder')` + await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) + VALUES ('folder', 'Docs', 'user-a', 'file', 'project-a')` + await sql`INSERT INTO workspace_files (id, context, user_id, project_id, folder_id) + VALUES ('file', 'project', 'user-a', 'project-a', 'folder')` await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` await applyMigration(migrations[1]) const [file] = await sql`SELECT folder_id, key, @@ -612,10 +559,10 @@ describe('file ownership composed with enforced Project membership', () => { } const [project] = await chain<{ id: string }[]>`SELECT id FROM project` const [before] = await chain`SELECT * FROM project WHERE id = ${project.id}` - await chain`INSERT INTO folder (id, name, user_id, resource_type, entity_type, entity_id) - VALUES ('folder', 'Architecture', 'creator', 'file', 'project', ${project.id})` - await chain`INSERT INTO workspace_files (id, context, user_id, entity_type, entity_id, folder_id) - VALUES ('file', 'project', 'creator', 'project', ${project.id}, 'folder')` + await chain`INSERT INTO folder (id, name, user_id, resource_type, project_id) + VALUES ('folder', 'Architecture', 'creator', 'file', ${project.id})` + await chain`INSERT INTO workspace_files (id, context, user_id, project_id, folder_id) + VALUES ('file', 'project', 'creator', ${project.id}, 'folder')` await chain`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` const [after] = await chain`SELECT * FROM project WHERE id = ${project.id}` expect(after).toEqual(before) diff --git a/packages/db/migrations/0396_file_entity_ownership.sql b/packages/db/migrations/0396_file_entity_ownership.sql index 303cc94e6e0..f54ffb7d794 100644 --- a/packages/db/migrations/0396_file_entity_ownership.sql +++ b/packages/db/migrations/0396_file_entity_ownership.sql @@ -1,129 +1,63 @@ --- Additive ownership metadata. Legacy columns/FKs remain authoritative for legacy writers. -ALTER TABLE "workspace_files" ADD COLUMN IF NOT EXISTS "entity_type" text;--> statement-breakpoint -ALTER TABLE "workspace_files" ADD COLUMN IF NOT EXISTS "entity_id" text;--> statement-breakpoint +ALTER TABLE workspace_files ADD COLUMN IF NOT EXISTS project_id text;--> statement-breakpoint DO $$ BEGIN - IF NOT EXISTS ( - SELECT 1 FROM pg_constraint - WHERE conname = 'workspace_files_entity_binding_check' - AND conrelid = 'workspace_files'::regclass - ) THEN - ALTER TABLE "workspace_files" ADD CONSTRAINT "workspace_files_entity_binding_check" - CHECK ( - (entity_type IS NULL AND entity_id IS NULL) - OR (entity_type IS NOT NULL AND entity_id IS NOT NULL - AND entity_type IN ('workspace', 'project', 'organization', 'user') - AND char_length(entity_id) > 0) - ) NOT VALID; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'workspace_files_project_id_project_id_fk' AND conrelid = 'workspace_files'::regclass) THEN + ALTER TABLE workspace_files ADD CONSTRAINT workspace_files_project_id_project_id_fk + FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE RESTRICT NOT VALID; + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'workspace_files_owner_check' AND conrelid = 'workspace_files'::regclass) THEN + ALTER TABLE workspace_files ADD CONSTRAINT workspace_files_owner_check + CHECK (num_nonnulls(workspace_id, project_id, organization_id) <= 1) NOT VALID; + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'workspace_files_project_binding_check' AND conrelid = 'workspace_files'::regclass) THEN + ALTER TABLE workspace_files ADD CONSTRAINT workspace_files_project_binding_check + CHECK ((project_id IS NOT NULL) = (context = 'project') AND (project_id IS NULL OR chat_id IS NULL)) NOT VALID; END IF; END $$;--> statement-breakpoint --- This mapping is deliberately incomplete. Unknown legacy contexts stay unbound until audited. -CREATE OR REPLACE FUNCTION workspace_file_legacy_entity( - file_context text, file_workspace_id text, file_organization_id text, file_user_id text +CREATE OR REPLACE FUNCTION workspace_file_owner( + file_context text, file_workspace_id text, file_project_id text, file_organization_id text, file_user_id text ) RETURNS TABLE (entity_type text, entity_id text) LANGUAGE sql IMMUTABLE AS $$ SELECT CASE - WHEN file_organization_id IS NULL AND file_workspace_id IS NOT NULL + WHEN file_project_id IS NOT NULL AND file_workspace_id IS NULL AND file_organization_id IS NULL + AND file_context = 'project' THEN 'project' + WHEN file_project_id IS NULL AND file_organization_id IS NULL AND file_workspace_id IS NOT NULL AND file_context IN ('workspace', 'mothership', 'execution', 'workspace-logos', 'knowledge-base') THEN 'workspace' - WHEN file_workspace_id IS NULL AND file_organization_id IS NOT NULL + WHEN file_project_id IS NULL AND file_workspace_id IS NULL AND file_organization_id IS NOT NULL AND file_context = 'knowledge-base' THEN 'organization' - WHEN file_workspace_id IS NULL AND file_organization_id IS NULL + WHEN file_project_id IS NULL AND file_workspace_id IS NULL AND file_organization_id IS NULL AND file_context IN ('copilot', 'profile-pictures') THEN 'user' END, CASE - WHEN file_organization_id IS NULL AND file_workspace_id IS NOT NULL + WHEN file_project_id IS NOT NULL AND file_workspace_id IS NULL AND file_organization_id IS NULL + AND file_context = 'project' THEN file_project_id + WHEN file_project_id IS NULL AND file_organization_id IS NULL AND file_workspace_id IS NOT NULL AND file_context IN ('workspace', 'mothership', 'execution', 'workspace-logos', 'knowledge-base') THEN file_workspace_id - WHEN file_workspace_id IS NULL AND file_organization_id IS NOT NULL + WHEN file_project_id IS NULL AND file_workspace_id IS NULL AND file_organization_id IS NOT NULL AND file_context = 'knowledge-base' THEN file_organization_id - WHEN file_workspace_id IS NULL AND file_organization_id IS NULL + WHEN file_project_id IS NULL AND file_workspace_id IS NULL AND file_organization_id IS NULL AND file_context IN ('copilot', 'profile-pictures') THEN file_user_id END $$;--> statement-breakpoint -CREATE OR REPLACE FUNCTION workspace_files_sync_entity_binding() +CREATE OR REPLACE FUNCTION workspace_files_preserve_project_owner() RETURNS trigger LANGUAGE plpgsql AS $$ -DECLARE - expected_type text; - expected_id text; - legacy_changed boolean := false; - pair_changed boolean := false; BEGIN - IF TG_OP = 'UPDATE' THEN - legacy_changed := ROW(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id) - IS DISTINCT FROM ROW(OLD.context, OLD.workspace_id, OLD.organization_id, OLD.user_id); - pair_changed := ROW(NEW.entity_type, NEW.entity_id) - IS DISTINCT FROM ROW(OLD.entity_type, OLD.entity_id); - IF OLD.entity_type = 'project' AND pair_changed THEN - RAISE EXCEPTION 'Project file ownership cannot change through a metadata update' - USING ERRCODE = '23514'; - END IF; - END IF; - - IF NEW.entity_type = 'project' THEN - IF NEW.entity_id IS NULL OR NEW.entity_id = '' OR NEW.context <> 'project' - OR NEW.workspace_id IS NOT NULL OR NEW.organization_id IS NOT NULL - OR NEW.chat_id IS NOT NULL OR NEW.folder_id IS NOT NULL THEN - RAISE EXCEPTION 'Project file binding conflicts with legacy ownership or an unsupported folder' - USING ERRCODE = '23514'; - END IF; - IF TG_OP = 'INSERT' OR OLD.entity_type IS DISTINCT FROM 'project' THEN - -- A new row version fences parent deletion from an older REPEATABLE READ snapshot. - -- A read-only parent lock cannot make that snapshot see this newly inserted child. - UPDATE project SET id = id WHERE id = NEW.entity_id; - ELSE - PERFORM 1 FROM project WHERE id = NEW.entity_id FOR KEY SHARE; - END IF; - IF NOT FOUND THEN - RAISE EXCEPTION 'Project file owner does not exist' USING ERRCODE = '23503'; - END IF; - RETURN NEW; - END IF; - - SELECT entity_type, entity_id INTO expected_type, expected_id - FROM workspace_file_legacy_entity(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id); - - IF (NEW.entity_type IS NULL AND NEW.entity_id IS NULL) - OR (TG_OP = 'UPDATE' AND legacy_changed AND NOT pair_changed) THEN - NEW.entity_type := expected_type; - NEW.entity_id := expected_id; - ELSIF ROW(NEW.entity_type, NEW.entity_id) IS DISTINCT FROM ROW(expected_type, expected_id) THEN - RAISE EXCEPTION 'File entity binding conflicts with its legacy owner or context' - USING ERRCODE = '23514'; - END IF; - - IF NEW.context = 'project' THEN - RAISE EXCEPTION 'Project files require explicit Project ownership' USING ERRCODE = '23514'; - END IF; - RETURN NEW; -END; -$$;--> statement-breakpoint -DROP TRIGGER IF EXISTS workspace_files_sync_entity_binding ON workspace_files;--> statement-breakpoint -CREATE TRIGGER workspace_files_sync_entity_binding -BEFORE INSERT OR UPDATE OF entity_type, entity_id, workspace_id, organization_id, user_id, context, folder_id, chat_id -ON workspace_files FOR EACH ROW EXECUTE FUNCTION workspace_files_sync_entity_binding();--> statement-breakpoint -CREATE OR REPLACE FUNCTION project_file_owner_delete_guard() -RETURNS trigger LANGUAGE plpgsql AS $$ -BEGIN - IF TG_OP = 'UPDATE' AND NEW.id = OLD.id THEN RETURN NEW; END IF; - IF EXISTS ( - SELECT 1 FROM workspace_files WHERE entity_type = 'project' AND entity_id = OLD.id - ) THEN - RAISE EXCEPTION 'Project files must be retired before their owner is removed' - USING ERRCODE = '23503'; + IF OLD.project_id IS NOT NULL AND NEW.project_id IS DISTINCT FROM OLD.project_id THEN + RAISE EXCEPTION 'Project file ownership cannot change through a metadata update' USING ERRCODE = '23514'; END IF; - IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; RETURN NEW; END; $$;--> statement-breakpoint -DROP TRIGGER IF EXISTS project_file_owner_delete_guard ON project;--> statement-breakpoint -CREATE TRIGGER project_file_owner_delete_guard -BEFORE DELETE OR UPDATE OF id ON project -FOR EACH ROW EXECUTE FUNCTION project_file_owner_delete_guard();--> statement-breakpoint +DROP TRIGGER IF EXISTS workspace_files_preserve_project_owner ON workspace_files;--> statement-breakpoint +CREATE TRIGGER workspace_files_preserve_project_owner +BEFORE UPDATE OF project_id ON workspace_files +FOR EACH ROW EXECUTE FUNCTION workspace_files_preserve_project_owner();--> statement-breakpoint CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF EXISTS ( - SELECT 1 FROM workspace_files WHERE entity_type = 'project' AND user_id = OLD.id + SELECT 1 FROM workspace_files WHERE project_id IS NOT NULL AND user_id = OLD.id ) THEN RAISE EXCEPTION 'Project file attribution must be reassigned or retired before deleting its uploader' USING ERRCODE = '23503'; @@ -138,7 +72,7 @@ FOR EACH ROW EXECUTE FUNCTION project_file_uploader_delete_guard();--> statement -- A failed concurrent build is replayable; do not retain an invalid same-name index. COMMIT;--> statement-breakpoint SET lock_timeout = 0;--> statement-breakpoint -DROP INDEX CONCURRENTLY IF EXISTS "workspace_files_entity_id_idx";--> statement-breakpoint -CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_entity_id_idx" -ON "workspace_files" USING btree ("entity_type", "entity_id", "id");--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "workspace_files_project_id_idx";--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_project_id_idx" +ON "workspace_files" USING btree ("project_id", "id");--> statement-breakpoint SET lock_timeout = '5s'; diff --git a/packages/db/migrations/0397_file_folder_version_ownership.sql b/packages/db/migrations/0397_file_folder_version_ownership.sql index e16243a33f0..b37f9482e15 100644 --- a/packages/db/migrations/0397_file_folder_version_ownership.sql +++ b/packages/db/migrations/0397_file_folder_version_ownership.sql @@ -1,74 +1,40 @@ --- Nullable compatibility fields preserve all legacy writers while adding Project file folders. -ALTER TABLE "folder" ADD COLUMN IF NOT EXISTS "entity_type" text;--> statement-breakpoint -ALTER TABLE "folder" ADD COLUMN IF NOT EXISTS "entity_id" text;--> statement-breakpoint -ALTER TABLE "folder" ALTER COLUMN "workspace_id" DROP NOT NULL;--> statement-breakpoint -ALTER TABLE "workspace_file_version" ALTER COLUMN "workspace_id" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE folder ADD COLUMN IF NOT EXISTS project_id text;--> statement-breakpoint +ALTER TABLE folder ALTER COLUMN workspace_id DROP NOT NULL;--> statement-breakpoint +ALTER TABLE workspace_file_version ALTER COLUMN workspace_id DROP NOT NULL;--> statement-breakpoint DO $$ BEGIN - IF NOT EXISTS ( - SELECT 1 FROM pg_constraint WHERE conname = 'folder_entity_binding_check' - AND conrelid = 'folder'::regclass - ) THEN - ALTER TABLE folder ADD CONSTRAINT folder_entity_binding_check CHECK ( - (entity_type IS NULL AND entity_id IS NULL AND workspace_id IS NOT NULL) - OR (entity_type IS NOT NULL AND entity_id IS NOT NULL AND char_length(entity_id) > 0 - AND ((entity_type = 'workspace' AND workspace_id IS NOT NULL AND entity_id = workspace_id) - OR (entity_type = 'project' AND workspace_id IS NULL AND resource_type = 'file'))) - ) NOT VALID; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_project_id_project_id_fk' AND conrelid = 'folder'::regclass) THEN + ALTER TABLE folder ADD CONSTRAINT folder_project_id_project_id_fk + FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE RESTRICT NOT VALID; + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_owner_check' AND conrelid = 'folder'::regclass) THEN + ALTER TABLE folder ADD CONSTRAINT folder_owner_check + CHECK (num_nonnulls(workspace_id, project_id) = 1 AND (project_id IS NULL OR resource_type = 'file')) NOT VALID; END IF; END $$;--> statement-breakpoint -CREATE OR REPLACE FUNCTION folder_sync_entity_binding() +CREATE OR REPLACE FUNCTION folder_validate_owner() RETURNS trigger LANGUAGE plpgsql AS $$ -DECLARE - previous_type text; - previous_id text; - hierarchy_changed boolean := true; - lock_key text; +DECLARE hierarchy_changed boolean := true; lock_key text; BEGIN - IF NEW.workspace_id IS NOT NULL THEN - IF NEW.entity_type IS NULL AND NEW.entity_id IS NULL THEN - NEW.entity_type := 'workspace'; - NEW.entity_id := NEW.workspace_id; - ELSIF NEW.entity_type IS DISTINCT FROM 'workspace' OR NEW.entity_id IS DISTINCT FROM NEW.workspace_id THEN - RAISE EXCEPTION 'Folder entity conflicts with its workspace' USING ERRCODE = '23514'; - END IF; - ELSIF NEW.entity_type IS DISTINCT FROM 'project' OR NEW.entity_id IS NULL OR NEW.entity_id = '' - OR NEW.resource_type <> 'file' THEN - RAISE EXCEPTION 'Only Project file folders may omit workspace ownership' USING ERRCODE = '23514'; - END IF; - IF TG_OP = 'UPDATE' THEN - previous_type := coalesce(OLD.entity_type, 'workspace'); - previous_id := coalesce(OLD.entity_id, OLD.workspace_id); - IF ROW(previous_type, previous_id, OLD.resource_type, OLD.id) - IS DISTINCT FROM ROW(NEW.entity_type, NEW.entity_id, NEW.resource_type, NEW.id) THEN + IF ROW(OLD.workspace_id, OLD.project_id, OLD.resource_type, OLD.id) + IS DISTINCT FROM ROW(NEW.workspace_id, NEW.project_id, NEW.resource_type, NEW.id) THEN RAISE EXCEPTION 'Folder identity, owner, and resource type cannot be changed' USING ERRCODE = '23514'; END IF; hierarchy_changed := NEW.parent_id IS DISTINCT FROM OLD.parent_id; END IF; - - IF NEW.entity_type = 'project' THEN - IF TG_OP = 'INSERT' THEN - -- Fence stale-snapshot Project deletion without changing public parent fields. - UPDATE project SET id = id WHERE id = NEW.entity_id; - ELSE - PERFORM 1 FROM project WHERE id = NEW.entity_id FOR KEY SHARE; - END IF; - IF NOT FOUND THEN RAISE EXCEPTION 'Project folder owner does not exist' USING ERRCODE = '23503'; END IF; - END IF; - IF hierarchy_changed THEN - -- Existing workspace writers already use this key before taking folder row locks. + -- Existing workspace writers use this key before taking folder row locks. lock_key := 'resource_folders:' || NEW.resource_type::text || ':' || - CASE WHEN NEW.entity_type = 'workspace' THEN NEW.entity_id ELSE NEW.entity_type || ':' || NEW.entity_id END; + coalesce(NEW.workspace_id, 'project:' || NEW.project_id); PERFORM pg_advisory_xact_lock(hashtextextended(lock_key, 0)); END IF; RETURN NEW; END; $$;--> statement-breakpoint -DROP TRIGGER IF EXISTS folder_entity_binding ON folder;--> statement-breakpoint -CREATE TRIGGER folder_entity_binding -BEFORE INSERT OR UPDATE OF id, entity_type, entity_id, workspace_id, resource_type, parent_id -ON folder FOR EACH ROW EXECUTE FUNCTION folder_sync_entity_binding();--> statement-breakpoint +DROP TRIGGER IF EXISTS folder_validate_owner ON folder;--> statement-breakpoint +CREATE TRIGGER folder_validate_owner +BEFORE INSERT OR UPDATE OF id, workspace_id, project_id, resource_type, parent_id +ON folder FOR EACH ROW EXECUTE FUNCTION folder_validate_owner();--> statement-breakpoint CREATE OR REPLACE FUNCTION folder_parent_resource_type_match() RETURNS trigger LANGUAGE plpgsql AS $$ DECLARE @@ -79,12 +45,10 @@ DECLARE BEGIN SELECT * INTO child FROM folder WHERE id = NEW.id; IF NOT FOUND THEN RETURN NEW; END IF; - child.entity_type := coalesce(child.entity_type, 'workspace'); - child.entity_id := coalesce(child.entity_id, child.workspace_id); IF TG_OP = 'UPDATE' AND OLD.parent_id IS NOT DISTINCT FROM child.parent_id AND OLD.resource_type = child.resource_type - AND ROW(coalesce(OLD.entity_type, 'workspace'), coalesce(OLD.entity_id, OLD.workspace_id)) - IS NOT DISTINCT FROM ROW(child.entity_type, child.entity_id) THEN + AND ROW(OLD.workspace_id, OLD.project_id) + IS NOT DISTINCT FROM ROW(child.workspace_id, child.project_id) THEN RETURN NEW; END IF; ancestor_id := child.parent_id; @@ -98,8 +62,8 @@ BEGIN SELECT * INTO ancestor FROM folder WHERE id = ancestor_id FOR SHARE; IF NOT FOUND THEN RAISE EXCEPTION 'Folder parent does not exist' USING ERRCODE = '23503'; END IF; IF ancestor.resource_type <> child.resource_type - OR ROW(coalesce(ancestor.entity_type, 'workspace'), coalesce(ancestor.entity_id, ancestor.workspace_id)) - IS DISTINCT FROM ROW(child.entity_type, child.entity_id) THEN + OR ROW(ancestor.workspace_id, ancestor.project_id) + IS DISTINCT FROM ROW(child.workspace_id, child.project_id) THEN RAISE EXCEPTION 'Folder parent belongs to a different owner or resource type' USING ERRCODE = '23514'; END IF; ancestor_id := ancestor.parent_id; @@ -112,63 +76,6 @@ DROP TRIGGER IF EXISTS folder_parent_resource_type_match ON folder;--> statement CREATE CONSTRAINT TRIGGER folder_parent_resource_type_match AFTER INSERT OR UPDATE ON folder DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE FUNCTION folder_parent_resource_type_match();--> statement-breakpoint -CREATE OR REPLACE FUNCTION workspace_files_sync_entity_binding() -RETURNS trigger LANGUAGE plpgsql AS $$ -DECLARE - expected_type text; - expected_id text; - legacy_changed boolean := false; - pair_changed boolean := false; -BEGIN - IF TG_OP = 'UPDATE' THEN - legacy_changed := ROW(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id) - IS DISTINCT FROM ROW(OLD.context, OLD.workspace_id, OLD.organization_id, OLD.user_id); - pair_changed := ROW(NEW.entity_type, NEW.entity_id) - IS DISTINCT FROM ROW(OLD.entity_type, OLD.entity_id); - IF OLD.entity_type = 'project' AND pair_changed THEN - RAISE EXCEPTION 'Project file ownership cannot change through a metadata update' - USING ERRCODE = '23514'; - END IF; - END IF; - - IF NEW.entity_type = 'project' THEN - IF NEW.entity_id IS NULL OR NEW.entity_id = '' OR NEW.context <> 'project' - OR NEW.workspace_id IS NOT NULL OR NEW.organization_id IS NOT NULL - OR NEW.chat_id IS NOT NULL THEN - RAISE EXCEPTION 'Project file binding conflicts with legacy ownership' - USING ERRCODE = '23514'; - END IF; - IF TG_OP = 'INSERT' OR OLD.entity_type IS DISTINCT FROM 'project' THEN - -- A new row version fences parent deletion from an older REPEATABLE READ snapshot. - -- A read-only parent lock cannot make that snapshot see this newly inserted child. - UPDATE project SET id = id WHERE id = NEW.entity_id; - ELSE - PERFORM 1 FROM project WHERE id = NEW.entity_id FOR KEY SHARE; - END IF; - IF NOT FOUND THEN - RAISE EXCEPTION 'Project file owner does not exist' USING ERRCODE = '23503'; - END IF; - RETURN NEW; - END IF; - - SELECT entity_type, entity_id INTO expected_type, expected_id - FROM workspace_file_legacy_entity(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id); - - IF (NEW.entity_type IS NULL AND NEW.entity_id IS NULL) - OR (TG_OP = 'UPDATE' AND legacy_changed AND NOT pair_changed) THEN - NEW.entity_type := expected_type; - NEW.entity_id := expected_id; - ELSIF ROW(NEW.entity_type, NEW.entity_id) IS DISTINCT FROM ROW(expected_type, expected_id) THEN - RAISE EXCEPTION 'File entity binding conflicts with its legacy owner or context' - USING ERRCODE = '23514'; - END IF; - - IF NEW.context = 'project' THEN - RAISE EXCEPTION 'Project files require explicit Project ownership' USING ERRCODE = '23514'; - END IF; - RETURN NEW; -END; -$$;--> statement-breakpoint CREATE OR REPLACE FUNCTION workspace_files_validate_relations() RETURNS trigger LANGUAGE plpgsql AS $$ DECLARE @@ -178,19 +85,11 @@ DECLARE current_id text; target folder%ROWTYPE; BEGIN - current_type := NEW.entity_type; - current_id := NEW.entity_id; - IF current_type IS NULL AND current_id IS NULL THEN - SELECT entity_type, entity_id INTO current_type, current_id - FROM workspace_file_legacy_entity(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id); - END IF; + SELECT entity_type, entity_id INTO current_type, current_id + FROM workspace_file_owner(NEW.context, NEW.workspace_id, NEW.project_id, NEW.organization_id, NEW.user_id); IF TG_OP = 'UPDATE' THEN - previous_type := OLD.entity_type; - previous_id := OLD.entity_id; - IF previous_type IS NULL AND previous_id IS NULL THEN - SELECT entity_type, entity_id INTO previous_type, previous_id - FROM workspace_file_legacy_entity(OLD.context, OLD.workspace_id, OLD.organization_id, OLD.user_id); - END IF; + SELECT entity_type, entity_id INTO previous_type, previous_id + FROM workspace_file_owner(OLD.context, OLD.workspace_id, OLD.project_id, OLD.organization_id, OLD.user_id); IF (ROW(previous_type, previous_id) IS DISTINCT FROM ROW(current_type, current_id) OR NEW.context NOT IN ('workspace', 'project')) AND EXISTS (SELECT 1 FROM workspace_file_version WHERE file_id = NEW.id) THEN @@ -205,8 +104,8 @@ BEGIN SELECT * INTO target FROM folder WHERE id = NEW.folder_id FOR SHARE; IF NOT FOUND THEN RAISE EXCEPTION 'File folder does not exist' USING ERRCODE = '23503'; END IF; IF target.resource_type <> 'file' OR current_type IS NULL OR current_id IS NULL - OR ROW(coalesce(target.entity_type, 'workspace'), coalesce(target.entity_id, target.workspace_id)) - IS DISTINCT FROM ROW(current_type, current_id) THEN + OR ROW(target.workspace_id, target.project_id) + IS DISTINCT FROM ROW(NEW.workspace_id, NEW.project_id) THEN RAISE EXCEPTION 'File folder belongs to a different owner or resource type' USING ERRCODE = '23514'; END IF; END IF; @@ -234,12 +133,8 @@ BEGIN SELECT * INTO parent FROM workspace_files WHERE id = NEW.file_id FOR SHARE; END IF; IF NOT FOUND THEN RAISE EXCEPTION 'Version file does not exist' USING ERRCODE = '23503'; END IF; - parent_type := parent.entity_type; - parent_id := parent.entity_id; - IF parent_type IS NULL AND parent_id IS NULL THEN - SELECT entity_type, entity_id INTO parent_type, parent_id - FROM workspace_file_legacy_entity(parent.context, parent.workspace_id, parent.organization_id, parent.user_id); - END IF; + SELECT entity_type, entity_id INTO parent_type, parent_id + FROM workspace_file_owner(parent.context, parent.workspace_id, parent.project_id, parent.organization_id, parent.user_id); IF parent_type = 'workspace' AND parent.context = 'workspace' THEN IF NEW.workspace_id IS NULL THEN NEW.workspace_id := parent_id; END IF; IF NEW.workspace_id IS DISTINCT FROM parent_id THEN @@ -257,24 +152,11 @@ DROP TRIGGER IF EXISTS workspace_file_version_owner_match ON workspace_file_vers CREATE TRIGGER workspace_file_version_owner_match BEFORE INSERT OR UPDATE OF file_id, workspace_id ON workspace_file_version FOR EACH ROW EXECUTE FUNCTION workspace_file_version_owner_match();--> statement-breakpoint -CREATE OR REPLACE FUNCTION project_file_owner_delete_guard() -RETURNS trigger LANGUAGE plpgsql AS $$ -BEGIN - IF TG_OP = 'UPDATE' AND NEW.id = OLD.id THEN RETURN NEW; END IF; - IF EXISTS (SELECT 1 FROM workspace_files WHERE entity_type = 'project' AND entity_id = OLD.id) - OR EXISTS (SELECT 1 FROM folder WHERE entity_type = 'project' AND entity_id = OLD.id) THEN - RAISE EXCEPTION 'Project files and folders must be retired before their owner is removed' - USING ERRCODE = '23503'; - END IF; - IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; - RETURN NEW; -END; -$$;--> statement-breakpoint CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN - IF EXISTS (SELECT 1 FROM workspace_files WHERE entity_type = 'project' AND user_id = OLD.id) - OR EXISTS (SELECT 1 FROM folder WHERE entity_type = 'project' AND user_id = OLD.id) THEN + IF EXISTS (SELECT 1 FROM workspace_files WHERE project_id IS NOT NULL AND user_id = OLD.id) + OR EXISTS (SELECT 1 FROM folder WHERE project_id IS NOT NULL AND user_id = OLD.id) THEN RAISE EXCEPTION 'Project file and folder attribution must be reassigned or retired before deleting its creator' USING ERRCODE = '23503'; END IF; @@ -283,15 +165,15 @@ END; $$;--> statement-breakpoint COMMIT;--> statement-breakpoint SET lock_timeout = 0;--> statement-breakpoint -DROP INDEX CONCURRENTLY IF EXISTS "folder_entity_id_idx";--> statement-breakpoint -CREATE INDEX CONCURRENTLY IF NOT EXISTS "folder_entity_id_idx" -ON "folder" USING btree ("entity_type", "entity_id", "id");--> statement-breakpoint -DROP INDEX CONCURRENTLY IF EXISTS "folder_entity_resource_parent_name_active_unique";--> statement-breakpoint -CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "folder_entity_resource_parent_name_active_unique" -ON "folder" USING btree ("entity_type", "entity_id", "resource_type", coalesce("parent_id", ''), "name") -WHERE "deleted_at" IS NULL;--> statement-breakpoint -DROP INDEX CONCURRENTLY IF EXISTS "workspace_files_entity_folder_name_active_unique";--> statement-breakpoint -CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_entity_folder_name_active_unique" -ON "workspace_files" USING btree ("entity_type", "entity_id", coalesce("folder_id", ''), "original_name") -WHERE "deleted_at" IS NULL AND "context" IN ('workspace', 'project');--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "folder_project_id_idx";--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "folder_project_id_idx" +ON "folder" USING btree ("project_id", "id");--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "folder_project_resource_parent_name_active_unique";--> statement-breakpoint +CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "folder_project_resource_parent_name_active_unique" +ON "folder" USING btree ("project_id", "resource_type", coalesce("parent_id", ''), "name") +WHERE "deleted_at" IS NULL AND "project_id" IS NOT NULL;--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "workspace_files_project_folder_name_active_unique";--> statement-breakpoint +CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_project_folder_name_active_unique" +ON "workspace_files" USING btree ("project_id", coalesce("folder_id", ''), "original_name") +WHERE "deleted_at" IS NULL AND "project_id" IS NOT NULL;--> statement-breakpoint SET lock_timeout = '5s'; diff --git a/packages/db/migrations/0398_file_creator_lifetime.sql b/packages/db/migrations/0398_file_creator_lifetime.sql index 6b00ae8e242..b39c357aa08 100644 --- a/packages/db/migrations/0398_file_creator_lifetime.sql +++ b/packages/db/migrations/0398_file_creator_lifetime.sql @@ -5,12 +5,12 @@ ALTER TABLE "workspace_files" ALTER COLUMN "user_id" DROP NOT NULL;--> statement DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_creator_lifetime_check' AND conrelid = 'folder'::regclass) THEN ALTER TABLE folder ADD CONSTRAINT folder_creator_lifetime_check CHECK ( - user_id IS NOT NULL OR coalesce(entity_type = 'project' AND char_length(original_creator_user_id) > 0, false) + user_id IS NOT NULL OR coalesce(project_id IS NOT NULL AND char_length(original_creator_user_id) > 0, false) ) NOT VALID; END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'workspace_files_creator_lifetime_check' AND conrelid = 'workspace_files'::regclass) THEN ALTER TABLE workspace_files ADD CONSTRAINT workspace_files_creator_lifetime_check CHECK ( - user_id IS NOT NULL OR coalesce(entity_type = 'project' AND char_length(original_creator_user_id) > 0, false) + user_id IS NOT NULL OR coalesce(project_id IS NOT NULL AND char_length(original_creator_user_id) > 0, false) ) NOT VALID; END IF; END $$;--> statement-breakpoint @@ -23,7 +23,7 @@ BEGIN IF NEW.user_id IS NULL THEN RAISE EXCEPTION 'New file resources require their actual creator' USING ERRCODE = '23514'; END IF; - IF NEW.entity_type = 'project' THEN + IF NEW.project_id IS NOT NULL THEN IF NEW.original_creator_user_id IS NOT NULL AND NEW.original_creator_user_id IS DISTINCT FROM NEW.user_id THEN RAISE EXCEPTION 'Project creator snapshot must match its actual creator' USING ERRCODE = '23514'; END IF; @@ -33,7 +33,7 @@ BEGIN END IF; ELSE original_creator := coalesce(OLD.original_creator_user_id, - CASE WHEN OLD.entity_type = 'project' OR NEW.entity_type = 'project' THEN OLD.user_id ELSE NULL END); + CASE WHEN OLD.project_id IS NOT NULL OR NEW.project_id IS NOT NULL THEN OLD.user_id ELSE NULL END); IF original_creator IS NOT NULL THEN IF (OLD.original_creator_user_id IS NOT NULL AND NEW.original_creator_user_id IS DISTINCT FROM OLD.original_creator_user_id) OR (NEW.original_creator_user_id IS NOT NULL AND NEW.original_creator_user_id IS DISTINCT FROM original_creator) @@ -45,7 +45,7 @@ BEGIN RAISE EXCEPTION 'Project original creator attribution is unavailable' USING ERRCODE = '23514'; END IF; END IF; - IF NEW.user_id IS NULL AND (NEW.entity_type IS DISTINCT FROM 'project' + IF NEW.user_id IS NULL AND (NEW.project_id IS NULL OR NEW.original_creator_user_id IS NULL OR NEW.original_creator_user_id = '') THEN RAISE EXCEPTION 'Only attributed Project resources may outlive their creator account' USING ERRCODE = '23514'; END IF; @@ -54,11 +54,11 @@ END; $$;--> statement-breakpoint DROP TRIGGER IF EXISTS folder_creator_lifetime ON folder;--> statement-breakpoint CREATE TRIGGER folder_creator_lifetime -BEFORE INSERT OR UPDATE OF user_id, original_creator_user_id, entity_type ON folder +BEFORE INSERT OR UPDATE OF user_id, original_creator_user_id, project_id ON folder FOR EACH ROW EXECUTE FUNCTION project_file_creator_snapshot();--> statement-breakpoint DROP TRIGGER IF EXISTS workspace_files_creator_lifetime ON workspace_files;--> statement-breakpoint CREATE TRIGGER workspace_files_creator_lifetime -BEFORE INSERT OR UPDATE OF user_id, original_creator_user_id, entity_type ON workspace_files +BEFORE INSERT OR UPDATE OF user_id, original_creator_user_id, project_id ON workspace_files FOR EACH ROW EXECUTE FUNCTION project_file_creator_snapshot();--> statement-breakpoint CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() RETURNS trigger LANGUAGE plpgsql AS $$ @@ -66,10 +66,10 @@ BEGIN -- Retain original authorship without substituting a new owner, payer, or creator. UPDATE workspace_files SET original_creator_user_id = coalesce(original_creator_user_id, user_id), user_id = NULL - WHERE entity_type = 'project' AND user_id = OLD.id; + WHERE project_id IS NOT NULL AND user_id = OLD.id; UPDATE folder SET original_creator_user_id = coalesce(original_creator_user_id, user_id), user_id = NULL - WHERE entity_type = 'project' AND user_id = OLD.id; + WHERE project_id IS NOT NULL AND user_id = OLD.id; RETURN OLD; END; $$;--> statement-breakpoint diff --git a/packages/db/migrations/0400_public_share_entity_ownership.sql b/packages/db/migrations/0400_public_share_entity_ownership.sql index dbacde95e50..c256694fb84 100644 --- a/packages/db/migrations/0400_public_share_entity_ownership.sql +++ b/packages/db/migrations/0400_public_share_entity_ownership.sql @@ -43,12 +43,8 @@ BEGIN IF NOT FOUND THEN RAISE EXCEPTION 'Public share file does not exist' USING ERRCODE = '23503'; END IF; - target_type := target.entity_type; - target_id := target.entity_id; - IF target_type IS NULL AND target_id IS NULL THEN - SELECT entity_type, entity_id INTO target_type, target_id - FROM workspace_file_legacy_entity(target.context, target.workspace_id, target.organization_id, target.user_id); - END IF; + SELECT entity_type, entity_id INTO target_type, target_id + FROM workspace_file_owner(target.context, target.workspace_id, target.project_id, target.organization_id, target.user_id); IF ROW(NEW.entity_type, NEW.entity_id) IS DISTINCT FROM ROW(target_type, target_id) THEN RAISE EXCEPTION 'Public share file belongs to a different or unknown owner' USING ERRCODE = '23514'; END IF; @@ -73,18 +69,10 @@ BEGIN DELETE FROM public_share WHERE resource_type = 'file' AND resource_id = OLD.id; RETURN OLD; END IF; - previous_type := OLD.entity_type; - previous_id := OLD.entity_id; - current_type := NEW.entity_type; - current_id := NEW.entity_id; - IF previous_type IS NULL AND previous_id IS NULL THEN - SELECT entity_type, entity_id INTO previous_type, previous_id - FROM workspace_file_legacy_entity(OLD.context, OLD.workspace_id, OLD.organization_id, OLD.user_id); - END IF; - IF current_type IS NULL AND current_id IS NULL THEN - SELECT entity_type, entity_id INTO current_type, current_id - FROM workspace_file_legacy_entity(NEW.context, NEW.workspace_id, NEW.organization_id, NEW.user_id); - END IF; + SELECT entity_type, entity_id INTO previous_type, previous_id + FROM workspace_file_owner(OLD.context, OLD.workspace_id, OLD.project_id, OLD.organization_id, OLD.user_id); + SELECT entity_type, entity_id INTO current_type, current_id + FROM workspace_file_owner(NEW.context, NEW.workspace_id, NEW.project_id, NEW.organization_id, NEW.user_id); IF NEW.id IS DISTINCT FROM OLD.id OR ROW(previous_type, previous_id) IS DISTINCT FROM ROW(current_type, current_id) THEN DELETE FROM public_share WHERE resource_type = 'file' AND resource_id = OLD.id; @@ -94,7 +82,7 @@ END; $$;--> statement-breakpoint DROP TRIGGER IF EXISTS workspace_file_retire_public_shares ON workspace_files;--> statement-breakpoint CREATE TRIGGER workspace_file_retire_public_shares -AFTER DELETE OR UPDATE OF id, entity_type, entity_id, workspace_id, organization_id, user_id, context +AFTER DELETE OR UPDATE OF id, project_id, workspace_id, organization_id, user_id, context ON workspace_files FOR EACH ROW EXECUTE FUNCTION workspace_file_retire_public_shares();--> statement-breakpoint COMMIT;--> statement-breakpoint SET lock_timeout = 0;--> statement-breakpoint diff --git a/packages/db/migrations/0401_file_search_owner_scope.sql b/packages/db/migrations/0401_file_search_owner_scope.sql index cbe5f52037f..7dab1fc2309 100644 --- a/packages/db/migrations/0401_file_search_owner_scope.sql +++ b/packages/db/migrations/0401_file_search_owner_scope.sql @@ -83,7 +83,7 @@ BEGIN SELECT coalesce(entity_type, 'workspace'), coalesce(entity_id, workspace_id) INTO target_type, target_id FROM workspace_file_search_build WHERE id = NEW.build_id; ELSE - SELECT coalesce(entity_type, 'workspace'), coalesce(entity_id, workspace_id) + SELECT CASE WHEN project_id IS NOT NULL THEN 'project' ELSE 'workspace' END, coalesce(project_id, workspace_id) INTO target_type, target_id FROM workspace_files WHERE id = NEW.file_id AND context IN ('workspace', 'project') FOR SHARE; END IF; @@ -116,8 +116,8 @@ BEGIN IF TG_OP = 'DELETE' THEN UPDATE workspace_file_search_build SET expires_at = now() WHERE id = (SELECT build_id FROM workspace_file_search_revision WHERE file_id = OLD.id); - owner_type := coalesce(OLD.entity_type, CASE WHEN OLD.context = 'workspace' THEN 'workspace' END); - owner_id := coalesce(OLD.entity_id, OLD.workspace_id); + owner_type := CASE WHEN OLD.project_id IS NOT NULL THEN 'project' WHEN OLD.context = 'workspace' THEN 'workspace' END; + owner_id := coalesce(OLD.project_id, OLD.workspace_id); IF owner_type IN ('workspace', 'project') AND OLD.context = owner_type AND owner_id IS NOT NULL THEN INSERT INTO file_search_dispatch_queue (entity_type, entity_id) VALUES (owner_type, owner_id) ON CONFLICT (entity_type, entity_id) DO UPDATE SET updated_at = now(); @@ -125,16 +125,16 @@ BEGIN RETURN OLD; END IF; IF TG_OP = 'UPDATE' THEN - IF ROW(NEW.content_updated_at, NEW.deleted_at, NEW.context, NEW.workspace_id, NEW.entity_type, NEW.entity_id) - IS NOT DISTINCT FROM ROW(OLD.content_updated_at, OLD.deleted_at, OLD.context, OLD.workspace_id, OLD.entity_type, OLD.entity_id) THEN + IF ROW(NEW.content_updated_at, NEW.deleted_at, NEW.context, NEW.workspace_id, NEW.project_id) + IS NOT DISTINCT FROM ROW(OLD.content_updated_at, OLD.deleted_at, OLD.context, OLD.workspace_id, OLD.project_id) THEN RETURN NEW; END IF; UPDATE workspace_file_search_build SET expires_at = now() WHERE id = (SELECT build_id FROM workspace_file_search_revision WHERE file_id = NEW.id); DELETE FROM workspace_file_search_revision WHERE file_id = NEW.id; END IF; - owner_type := coalesce(NEW.entity_type, CASE WHEN NEW.context = 'workspace' THEN 'workspace' END); - owner_id := coalesce(NEW.entity_id, NEW.workspace_id); + owner_type := CASE WHEN NEW.project_id IS NOT NULL THEN 'project' WHEN NEW.context = 'workspace' THEN 'workspace' END; + owner_id := coalesce(NEW.project_id, NEW.workspace_id); IF owner_type IN ('workspace', 'project') AND NEW.context = owner_type AND owner_id IS NOT NULL THEN -- Waking the owner also reconciles bounded dependent builds without rewriting their sources here. INSERT INTO file_search_dispatch_queue (entity_type, entity_id) VALUES (owner_type, owner_id) @@ -163,6 +163,6 @@ CREATE INDEX CONCURRENTLY IF NOT EXISTS "file_search_revision_owner_pending_idx" --> statement-breakpoint CREATE INDEX CONCURRENTLY IF NOT EXISTS "file_search_revision_owner_status_idx" ON "workspace_file_search_revision" USING btree (coalesce("entity_type", 'workspace'),coalesce("entity_id", "workspace_id"),"status","dispatched_at"); --> statement-breakpoint -CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_search_owner_keyset_idx" ON "workspace_files" USING btree (coalesce("entity_type", 'workspace'),coalesce("entity_id", "workspace_id"),"id") WHERE "workspace_files"."deleted_at" IS NULL AND (("workspace_files"."context" = 'workspace' AND "workspace_files"."workspace_id" IS NOT NULL) OR ("workspace_files"."context" = 'project' AND "workspace_files"."entity_type" = 'project' AND "workspace_files"."entity_id" IS NOT NULL)); +CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_search_owner_keyset_idx" ON "workspace_files" USING btree ((CASE WHEN "project_id" IS NOT NULL THEN 'project' ELSE 'workspace' END),coalesce("project_id", "workspace_id"),"id") WHERE "workspace_files"."deleted_at" IS NULL AND (("workspace_files"."context" = 'workspace' AND "workspace_files"."workspace_id" IS NOT NULL) OR ("workspace_files"."context" = 'project' AND "workspace_files"."project_id" IS NOT NULL)); --> statement-breakpoint SET lock_timeout = '5s'; diff --git a/packages/db/migrations/meta/0396_snapshot.json b/packages/db/migrations/meta/0396_snapshot.json index 257db0f0a22..7c3b9b5821b 100644 --- a/packages/db/migrations/meta/0396_snapshot.json +++ b/packages/db/migrations/meta/0396_snapshot.json @@ -28130,18 +28130,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "key": { "name": "key", "type": "text", @@ -28258,36 +28246,15 @@ "type": "integer", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "workspace_files_entity_id_idx": { - "name": "workspace_files_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, "workspace_files_key_active_unique": { "name": "workspace_files_key_active_unique", "columns": [ @@ -28517,6 +28484,27 @@ "concurrently": false, "method": "btree", "with": {} + }, + "workspace_files_project_id_idx": { + "name": "workspace_files_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -28564,19 +28552,32 @@ "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" + }, + "workspace_files_project_id_project_id_fk": { + "name": "workspace_files_project_id_project_id_fk", + "tableFrom": "workspace_files", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "workspace_files_entity_binding_check": { - "name": "workspace_files_entity_binding_check", - "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" - }, "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)" + }, + "workspace_files_owner_check": { + "name": "workspace_files_owner_check", + "value": "num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\", \"workspace_files\".\"organization_id\") <= 1" + }, + "workspace_files_project_binding_check": { + "name": "workspace_files_project_binding_check", + "value": "(\"workspace_files\".\"project_id\" IS NOT NULL) = (\"workspace_files\".\"context\" = 'project') AND (\"workspace_files\".\"project_id\" IS NULL OR \"workspace_files\".\"chat_id\" IS NULL)" } }, "isRLSEnabled": false diff --git a/packages/db/migrations/meta/0397_snapshot.json b/packages/db/migrations/meta/0397_snapshot.json index dc25d6c01fa..5dd339ce551 100644 --- a/packages/db/migrations/meta/0397_snapshot.json +++ b/packages/db/migrations/meta/0397_snapshot.json @@ -8528,18 +8528,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "resource_type": { "name": "resource_type", "type": "folder_resource_type", @@ -8604,76 +8592,15 @@ "type": "timestamp", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "folder_entity_id_idx": { - "name": "folder_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_entity_resource_parent_name_active_unique": { - "name": "folder_entity_resource_parent_name_active_unique", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_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": true, - "method": "btree", - "with": {} - }, "folder_user_idx": { "name": "folder_user_idx", "columns": [ @@ -8807,6 +8734,61 @@ "concurrently": false, "method": "btree", "with": {} + }, + "folder_project_id_idx": { + "name": "folder_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_resource_parent_name_active_unique": { + "name": "folder_project_resource_parent_name_active_unique", + "columns": [ + { + "expression": "project_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 AND \"folder\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -8836,15 +8818,24 @@ "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" + }, + "folder_project_id_project_id_fk": { + "name": "folder_project_id_project_id_fk", + "tableFrom": "folder", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "folder_entity_binding_check": { - "name": "folder_entity_binding_check", - "value": "(\"folder\".\"entity_type\" IS NULL AND \"folder\".\"entity_id\" IS NULL AND \"folder\".\"workspace_id\" IS NOT NULL) OR (\"folder\".\"entity_type\" IS NOT NULL AND \"folder\".\"entity_id\" IS NOT NULL AND char_length(\"folder\".\"entity_id\") > 0 AND ((\"folder\".\"entity_type\" = 'workspace' AND \"folder\".\"workspace_id\" IS NOT NULL AND \"folder\".\"entity_id\" = \"folder\".\"workspace_id\") OR (\"folder\".\"entity_type\" = 'project' AND \"folder\".\"workspace_id\" IS NULL AND \"folder\".\"resource_type\" = 'file')))" + "folder_owner_check": { + "name": "folder_owner_check", + "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" } }, "isRLSEnabled": false @@ -28214,18 +28205,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "key": { "name": "key", "type": "text", @@ -28342,70 +28321,15 @@ "type": "integer", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "workspace_files_entity_id_idx": { - "name": "workspace_files_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "workspace_files_entity_folder_name_active_unique": { - "name": "workspace_files_entity_folder_name_active_unique", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_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\" IN ('workspace', 'project')", - "concurrently": true, - "method": "btree", - "with": {} - }, "workspace_files_key_active_unique": { "name": "workspace_files_key_active_unique", "columns": [ @@ -28635,6 +28559,55 @@ "concurrently": false, "method": "btree", "with": {} + }, + "workspace_files_project_id_idx": { + "name": "workspace_files_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_project_folder_name_active_unique": { + "name": "workspace_files_project_folder_name_active_unique", + "columns": [ + { + "expression": "project_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\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -28682,19 +28655,32 @@ "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" + }, + "workspace_files_project_id_project_id_fk": { + "name": "workspace_files_project_id_project_id_fk", + "tableFrom": "workspace_files", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "workspace_files_entity_binding_check": { - "name": "workspace_files_entity_binding_check", - "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" - }, "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)" + }, + "workspace_files_owner_check": { + "name": "workspace_files_owner_check", + "value": "num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\", \"workspace_files\".\"organization_id\") <= 1" + }, + "workspace_files_project_binding_check": { + "name": "workspace_files_project_binding_check", + "value": "(\"workspace_files\".\"project_id\" IS NOT NULL) = (\"workspace_files\".\"context\" = 'project') AND (\"workspace_files\".\"project_id\" IS NULL OR \"workspace_files\".\"chat_id\" IS NULL)" } }, "isRLSEnabled": false diff --git a/packages/db/migrations/meta/0398_snapshot.json b/packages/db/migrations/meta/0398_snapshot.json index a06a9efaa60..044a74d6e5f 100644 --- a/packages/db/migrations/meta/0398_snapshot.json +++ b/packages/db/migrations/meta/0398_snapshot.json @@ -8528,18 +8528,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "resource_type": { "name": "resource_type", "type": "folder_resource_type", @@ -8610,76 +8598,15 @@ "type": "timestamp", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "folder_entity_id_idx": { - "name": "folder_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_entity_resource_parent_name_active_unique": { - "name": "folder_entity_resource_parent_name_active_unique", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_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": true, - "method": "btree", - "with": {} - }, "folder_user_idx": { "name": "folder_user_idx", "columns": [ @@ -8813,6 +8740,61 @@ "concurrently": false, "method": "btree", "with": {} + }, + "folder_project_id_idx": { + "name": "folder_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_resource_parent_name_active_unique": { + "name": "folder_project_resource_parent_name_active_unique", + "columns": [ + { + "expression": "project_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 AND \"folder\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -8842,6 +8824,15 @@ "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" + }, + "folder_project_id_project_id_fk": { + "name": "folder_project_id_project_id_fk", + "tableFrom": "folder", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -8850,11 +8841,11 @@ "checkConstraints": { "folder_creator_lifetime_check": { "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"entity_type\" = 'project' AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"project_id\" IS NOT NULL AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" }, - "folder_entity_binding_check": { - "name": "folder_entity_binding_check", - "value": "(\"folder\".\"entity_type\" IS NULL AND \"folder\".\"entity_id\" IS NULL AND \"folder\".\"workspace_id\" IS NOT NULL) OR (\"folder\".\"entity_type\" IS NOT NULL AND \"folder\".\"entity_id\" IS NOT NULL AND char_length(\"folder\".\"entity_id\") > 0 AND ((\"folder\".\"entity_type\" = 'workspace' AND \"folder\".\"workspace_id\" IS NOT NULL AND \"folder\".\"entity_id\" = \"folder\".\"workspace_id\") OR (\"folder\".\"entity_type\" = 'project' AND \"folder\".\"workspace_id\" IS NULL AND \"folder\".\"resource_type\" = 'file')))" + "folder_owner_check": { + "name": "folder_owner_check", + "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" } }, "isRLSEnabled": false @@ -28224,18 +28215,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "key": { "name": "key", "type": "text", @@ -28358,70 +28337,15 @@ "type": "integer", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "workspace_files_entity_id_idx": { - "name": "workspace_files_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "workspace_files_entity_folder_name_active_unique": { - "name": "workspace_files_entity_folder_name_active_unique", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_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\" IN ('workspace', 'project')", - "concurrently": true, - "method": "btree", - "with": {} - }, "workspace_files_key_active_unique": { "name": "workspace_files_key_active_unique", "columns": [ @@ -28651,6 +28575,55 @@ "concurrently": false, "method": "btree", "with": {} + }, + "workspace_files_project_id_idx": { + "name": "workspace_files_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_project_folder_name_active_unique": { + "name": "workspace_files_project_folder_name_active_unique", + "columns": [ + { + "expression": "project_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\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -28698,6 +28671,15 @@ "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" + }, + "workspace_files_project_id_project_id_fk": { + "name": "workspace_files_project_id_project_id_fk", + "tableFrom": "workspace_files", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -28706,15 +28688,19 @@ "checkConstraints": { "workspace_files_creator_lifetime_check": { "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"entity_type\" = 'project' AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" - }, - "workspace_files_entity_binding_check": { - "name": "workspace_files_entity_binding_check", - "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"project_id\" IS NOT NULL AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" }, "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)" + }, + "workspace_files_owner_check": { + "name": "workspace_files_owner_check", + "value": "num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\", \"workspace_files\".\"organization_id\") <= 1" + }, + "workspace_files_project_binding_check": { + "name": "workspace_files_project_binding_check", + "value": "(\"workspace_files\".\"project_id\" IS NOT NULL) = (\"workspace_files\".\"context\" = 'project') AND (\"workspace_files\".\"project_id\" IS NULL OR \"workspace_files\".\"chat_id\" IS NULL)" } }, "isRLSEnabled": false diff --git a/packages/db/migrations/meta/0399_snapshot.json b/packages/db/migrations/meta/0399_snapshot.json index b98f05a3e0b..bf465693d60 100644 --- a/packages/db/migrations/meta/0399_snapshot.json +++ b/packages/db/migrations/meta/0399_snapshot.json @@ -8528,18 +8528,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "resource_type": { "name": "resource_type", "type": "folder_resource_type", @@ -8610,76 +8598,15 @@ "type": "timestamp", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "folder_entity_id_idx": { - "name": "folder_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_entity_resource_parent_name_active_unique": { - "name": "folder_entity_resource_parent_name_active_unique", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_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": true, - "method": "btree", - "with": {} - }, "folder_user_idx": { "name": "folder_user_idx", "columns": [ @@ -8813,6 +8740,61 @@ "concurrently": false, "method": "btree", "with": {} + }, + "folder_project_id_idx": { + "name": "folder_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_resource_parent_name_active_unique": { + "name": "folder_project_resource_parent_name_active_unique", + "columns": [ + { + "expression": "project_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 AND \"folder\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -8842,6 +8824,15 @@ "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" + }, + "folder_project_id_project_id_fk": { + "name": "folder_project_id_project_id_fk", + "tableFrom": "folder", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -8850,11 +8841,11 @@ "checkConstraints": { "folder_creator_lifetime_check": { "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"entity_type\" = 'project' AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"project_id\" IS NOT NULL AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" }, - "folder_entity_binding_check": { - "name": "folder_entity_binding_check", - "value": "(\"folder\".\"entity_type\" IS NULL AND \"folder\".\"entity_id\" IS NULL AND \"folder\".\"workspace_id\" IS NOT NULL) OR (\"folder\".\"entity_type\" IS NOT NULL AND \"folder\".\"entity_id\" IS NOT NULL AND char_length(\"folder\".\"entity_id\") > 0 AND ((\"folder\".\"entity_type\" = 'workspace' AND \"folder\".\"workspace_id\" IS NOT NULL AND \"folder\".\"entity_id\" = \"folder\".\"workspace_id\") OR (\"folder\".\"entity_type\" = 'project' AND \"folder\".\"workspace_id\" IS NULL AND \"folder\".\"resource_type\" = 'file')))" + "folder_owner_check": { + "name": "folder_owner_check", + "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" } }, "isRLSEnabled": false @@ -28224,18 +28215,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "key": { "name": "key", "type": "text", @@ -28358,70 +28337,15 @@ "type": "integer", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "workspace_files_entity_id_idx": { - "name": "workspace_files_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "workspace_files_entity_folder_name_active_unique": { - "name": "workspace_files_entity_folder_name_active_unique", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_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\" IN ('workspace', 'project')", - "concurrently": true, - "method": "btree", - "with": {} - }, "workspace_files_key_active_unique": { "name": "workspace_files_key_active_unique", "columns": [ @@ -28651,6 +28575,55 @@ "concurrently": false, "method": "btree", "with": {} + }, + "workspace_files_project_id_idx": { + "name": "workspace_files_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_project_folder_name_active_unique": { + "name": "workspace_files_project_folder_name_active_unique", + "columns": [ + { + "expression": "project_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\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -28698,6 +28671,15 @@ "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" + }, + "workspace_files_project_id_project_id_fk": { + "name": "workspace_files_project_id_project_id_fk", + "tableFrom": "workspace_files", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -28706,15 +28688,19 @@ "checkConstraints": { "workspace_files_creator_lifetime_check": { "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"entity_type\" = 'project' AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" - }, - "workspace_files_entity_binding_check": { - "name": "workspace_files_entity_binding_check", - "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"project_id\" IS NOT NULL AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" }, "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)" + }, + "workspace_files_owner_check": { + "name": "workspace_files_owner_check", + "value": "num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\", \"workspace_files\".\"organization_id\") <= 1" + }, + "workspace_files_project_binding_check": { + "name": "workspace_files_project_binding_check", + "value": "(\"workspace_files\".\"project_id\" IS NOT NULL) = (\"workspace_files\".\"context\" = 'project') AND (\"workspace_files\".\"project_id\" IS NULL OR \"workspace_files\".\"chat_id\" IS NULL)" } }, "isRLSEnabled": false diff --git a/packages/db/migrations/meta/0400_snapshot.json b/packages/db/migrations/meta/0400_snapshot.json index 34542b6572e..212d7d05a0f 100644 --- a/packages/db/migrations/meta/0400_snapshot.json +++ b/packages/db/migrations/meta/0400_snapshot.json @@ -8528,18 +8528,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "resource_type": { "name": "resource_type", "type": "folder_resource_type", @@ -8610,76 +8598,15 @@ "type": "timestamp", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "folder_entity_id_idx": { - "name": "folder_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_entity_resource_parent_name_active_unique": { - "name": "folder_entity_resource_parent_name_active_unique", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_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": true, - "method": "btree", - "with": {} - }, "folder_user_idx": { "name": "folder_user_idx", "columns": [ @@ -8813,6 +8740,61 @@ "concurrently": false, "method": "btree", "with": {} + }, + "folder_project_id_idx": { + "name": "folder_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_resource_parent_name_active_unique": { + "name": "folder_project_resource_parent_name_active_unique", + "columns": [ + { + "expression": "project_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 AND \"folder\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -8842,6 +8824,15 @@ "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" + }, + "folder_project_id_project_id_fk": { + "name": "folder_project_id_project_id_fk", + "tableFrom": "folder", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -8850,11 +8841,11 @@ "checkConstraints": { "folder_creator_lifetime_check": { "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"entity_type\" = 'project' AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"project_id\" IS NOT NULL AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" }, - "folder_entity_binding_check": { - "name": "folder_entity_binding_check", - "value": "(\"folder\".\"entity_type\" IS NULL AND \"folder\".\"entity_id\" IS NULL AND \"folder\".\"workspace_id\" IS NOT NULL) OR (\"folder\".\"entity_type\" IS NOT NULL AND \"folder\".\"entity_id\" IS NOT NULL AND char_length(\"folder\".\"entity_id\") > 0 AND ((\"folder\".\"entity_type\" = 'workspace' AND \"folder\".\"workspace_id\" IS NOT NULL AND \"folder\".\"entity_id\" = \"folder\".\"workspace_id\") OR (\"folder\".\"entity_type\" = 'project' AND \"folder\".\"workspace_id\" IS NULL AND \"folder\".\"resource_type\" = 'file')))" + "folder_owner_check": { + "name": "folder_owner_check", + "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" } }, "isRLSEnabled": false @@ -28268,18 +28259,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "key": { "name": "key", "type": "text", @@ -28402,70 +28381,15 @@ "type": "integer", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "workspace_files_entity_id_idx": { - "name": "workspace_files_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "workspace_files_entity_folder_name_active_unique": { - "name": "workspace_files_entity_folder_name_active_unique", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_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\" IN ('workspace', 'project')", - "concurrently": true, - "method": "btree", - "with": {} - }, "workspace_files_key_active_unique": { "name": "workspace_files_key_active_unique", "columns": [ @@ -28695,6 +28619,55 @@ "concurrently": false, "method": "btree", "with": {} + }, + "workspace_files_project_id_idx": { + "name": "workspace_files_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_project_folder_name_active_unique": { + "name": "workspace_files_project_folder_name_active_unique", + "columns": [ + { + "expression": "project_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\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -28742,6 +28715,15 @@ "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" + }, + "workspace_files_project_id_project_id_fk": { + "name": "workspace_files_project_id_project_id_fk", + "tableFrom": "workspace_files", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -28750,15 +28732,19 @@ "checkConstraints": { "workspace_files_creator_lifetime_check": { "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"entity_type\" = 'project' AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" - }, - "workspace_files_entity_binding_check": { - "name": "workspace_files_entity_binding_check", - "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"project_id\" IS NOT NULL AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" }, "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)" + }, + "workspace_files_owner_check": { + "name": "workspace_files_owner_check", + "value": "num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\", \"workspace_files\".\"organization_id\") <= 1" + }, + "workspace_files_project_binding_check": { + "name": "workspace_files_project_binding_check", + "value": "(\"workspace_files\".\"project_id\" IS NOT NULL) = (\"workspace_files\".\"context\" = 'project') AND (\"workspace_files\".\"project_id\" IS NULL OR \"workspace_files\".\"chat_id\" IS NULL)" } }, "isRLSEnabled": false diff --git a/packages/db/migrations/meta/0401_snapshot.json b/packages/db/migrations/meta/0401_snapshot.json index 4e64b2788c0..e4ae12fc80e 100644 --- a/packages/db/migrations/meta/0401_snapshot.json +++ b/packages/db/migrations/meta/0401_snapshot.json @@ -8691,18 +8691,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "resource_type": { "name": "resource_type", "type": "folder_resource_type", @@ -8773,76 +8761,15 @@ "type": "timestamp", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "folder_entity_id_idx": { - "name": "folder_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_entity_resource_parent_name_active_unique": { - "name": "folder_entity_resource_parent_name_active_unique", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_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": true, - "method": "btree", - "with": {} - }, "folder_user_idx": { "name": "folder_user_idx", "columns": [ @@ -8976,6 +8903,61 @@ "concurrently": false, "method": "btree", "with": {} + }, + "folder_project_id_idx": { + "name": "folder_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_resource_parent_name_active_unique": { + "name": "folder_project_resource_parent_name_active_unique", + "columns": [ + { + "expression": "project_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 AND \"folder\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -9005,6 +8987,15 @@ "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" + }, + "folder_project_id_project_id_fk": { + "name": "folder_project_id_project_id_fk", + "tableFrom": "folder", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -9013,11 +9004,11 @@ "checkConstraints": { "folder_creator_lifetime_check": { "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"entity_type\" = 'project' AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"project_id\" IS NOT NULL AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" }, - "folder_entity_binding_check": { - "name": "folder_entity_binding_check", - "value": "(\"folder\".\"entity_type\" IS NULL AND \"folder\".\"entity_id\" IS NULL AND \"folder\".\"workspace_id\" IS NOT NULL) OR (\"folder\".\"entity_type\" IS NOT NULL AND \"folder\".\"entity_id\" IS NOT NULL AND char_length(\"folder\".\"entity_id\") > 0 AND ((\"folder\".\"entity_type\" = 'workspace' AND \"folder\".\"workspace_id\" IS NOT NULL AND \"folder\".\"entity_id\" = \"folder\".\"workspace_id\") OR (\"folder\".\"entity_type\" = 'project' AND \"folder\".\"workspace_id\" IS NULL AND \"folder\".\"resource_type\" = 'file')))" + "folder_owner_check": { + "name": "folder_owner_check", + "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" } }, "isRLSEnabled": false @@ -28605,18 +28596,6 @@ "primaryKey": true, "notNull": true }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "key": { "name": "key", "type": "text", @@ -28739,70 +28718,15 @@ "type": "integer", "primaryKey": false, "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { - "workspace_files_entity_id_idx": { - "name": "workspace_files_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "workspace_files_entity_folder_name_active_unique": { - "name": "workspace_files_entity_folder_name_active_unique", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_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\" IN ('workspace', 'project')", - "concurrently": true, - "method": "btree", - "with": {} - }, "workspace_files_key_active_unique": { "name": "workspace_files_key_active_unique", "columns": [ @@ -28851,13 +28775,13 @@ "name": "workspace_files_search_owner_keyset_idx", "columns": [ { - "expression": "coalesce(\"entity_type\", 'workspace')", + "expression": "CASE WHEN \"project_id\" IS NOT NULL THEN 'project' ELSE 'workspace' END", "asc": true, "isExpression": true, "nulls": "last" }, { - "expression": "coalesce(\"entity_id\", \"workspace_id\")", + "expression": "coalesce(\"project_id\", \"workspace_id\")", "asc": true, "isExpression": true, "nulls": "last" @@ -28870,7 +28794,7 @@ } ], "isUnique": false, - "where": "\"workspace_files\".\"deleted_at\" IS NULL AND ((\"workspace_files\".\"context\" = 'workspace' AND \"workspace_files\".\"workspace_id\" IS NOT NULL) OR (\"workspace_files\".\"context\" = 'project' AND \"workspace_files\".\"entity_type\" = 'project' AND \"workspace_files\".\"entity_id\" IS NOT NULL))", + "where": "\"workspace_files\".\"deleted_at\" IS NULL AND ((\"workspace_files\".\"context\" = 'workspace' AND \"workspace_files\".\"workspace_id\" IS NOT NULL) OR (\"workspace_files\".\"context\" = 'project' AND \"workspace_files\".\"project_id\" IS NOT NULL))", "concurrently": true, "method": "btree", "with": {} @@ -29060,6 +28984,55 @@ "concurrently": false, "method": "btree", "with": {} + }, + "workspace_files_project_id_idx": { + "name": "workspace_files_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_project_folder_name_active_unique": { + "name": "workspace_files_project_folder_name_active_unique", + "columns": [ + { + "expression": "project_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\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -29107,6 +29080,15 @@ "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" + }, + "workspace_files_project_id_project_id_fk": { + "name": "workspace_files_project_id_project_id_fk", + "tableFrom": "workspace_files", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -29115,15 +29097,19 @@ "checkConstraints": { "workspace_files_creator_lifetime_check": { "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"entity_type\" = 'project' AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" - }, - "workspace_files_entity_binding_check": { - "name": "workspace_files_entity_binding_check", - "value": "(\"workspace_files\".\"entity_type\" IS NULL AND \"workspace_files\".\"entity_id\" IS NULL) OR (\"workspace_files\".\"entity_type\" IS NOT NULL AND \"workspace_files\".\"entity_id\" IS NOT NULL AND \"workspace_files\".\"entity_type\" IN ('workspace', 'project', 'organization', 'user') AND char_length(\"workspace_files\".\"entity_id\") > 0)" + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"project_id\" IS NOT NULL AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" }, "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)" + }, + "workspace_files_owner_check": { + "name": "workspace_files_owner_check", + "value": "num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\", \"workspace_files\".\"organization_id\") <= 1" + }, + "workspace_files_project_binding_check": { + "name": "workspace_files_project_binding_check", + "value": "(\"workspace_files\".\"project_id\" IS NOT NULL) = (\"workspace_files\".\"context\" = 'project') AND (\"workspace_files\".\"project_id\" IS NULL OR \"workspace_files\".\"chat_id\" IS NULL)" } }, "isRLSEnabled": false diff --git a/packages/db/public-share-ownership.integration.ts b/packages/db/public-share-ownership.integration.ts index 9cddf286587..e0986073cc1 100644 --- a/packages/db/public-share-ownership.integration.ts +++ b/packages/db/public-share-ownership.integration.ts @@ -136,8 +136,8 @@ describe('Public share canonical ownership in PostgreSQL', () => { }) async function file(id = 'file', owner = 'project-a', kind: 'project' | 'workspace' = 'project') { - await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, entity_type, entity_id, original_name) - VALUES (${id}, ${kind}, 'user-a', ${kind === 'workspace' ? owner : null}, ${kind}, ${owner}, ${id})` + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, project_id, original_name) + VALUES (${id}, ${kind}, 'user-a', ${kind === 'workspace' ? owner : null}, ${kind === 'project' ? owner : null}, ${id})` } function share( executor: Sql, diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 0d138cf517e..f6aa68676ed 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -230,9 +230,9 @@ export const folder = pgTable( { id: text('id').primaryKey(), /** File folders may belong to a Project; other resource folders remain workspace-owned. */ - // contract-pending(after folder entity backfill and owner-aware readers are deployed): require both ownership fields. - entityType: text('entity_type'), - entityId: text('entity_id'), + projectId: text('project_id').references((): AnyPgColumn => project.id, { + onDelete: 'restrict', + }), resourceType: folderResourceTypeEnum('resource_type').notNull(), name: text('name').notNull(), /** Project creator references clear on account deletion; legacy folders retain their cascade. */ @@ -252,27 +252,19 @@ export const folder = pgTable( (table) => ({ creatorLifetimeCheck: check( 'folder_creator_lifetime_check', - sql`${table.userId} IS NOT NULL OR coalesce(${table.entityType} = 'project' AND char_length(${table.originalCreatorUserId}) > 0, false)` + sql`${table.userId} IS NOT NULL OR coalesce(${table.projectId} IS NOT NULL AND char_length(${table.originalCreatorUserId}) > 0, false)` ), - entityBindingCheck: check( - 'folder_entity_binding_check', - sql`(${table.entityType} IS NULL AND ${table.entityId} IS NULL AND ${table.workspaceId} IS NOT NULL) OR (${table.entityType} IS NOT NULL AND ${table.entityId} IS NOT NULL AND char_length(${table.entityId}) > 0 AND ((${table.entityType} = 'workspace' AND ${table.workspaceId} IS NOT NULL AND ${table.entityId} = ${table.workspaceId}) OR (${table.entityType} = 'project' AND ${table.workspaceId} IS NULL AND ${table.resourceType} = 'file')))` + ownerCheck: check( + 'folder_owner_check', + sql`num_nonnulls(${table.workspaceId}, ${table.projectId}) = 1 AND (${table.projectId} IS NULL OR ${table.resourceType} = 'file')` ), - entityIdIdx: index('folder_entity_id_idx') - .on(table.entityType, table.entityId, table.id) - .concurrently(), - entityResourceParentNameActiveUnique: uniqueIndex( - 'folder_entity_resource_parent_name_active_unique' + projectIdIdx: index('folder_project_id_idx').on(table.projectId, table.id).concurrently(), + projectResourceParentNameActiveUnique: uniqueIndex( + 'folder_project_resource_parent_name_active_unique' ) - .on( - table.entityType, - table.entityId, - table.resourceType, - sql`coalesce(${table.parentId}, '')`, - table.name - ) + .on(table.projectId, table.resourceType, sql`coalesce(${table.parentId}, '')`, table.name) .concurrently() - .where(sql`${table.deletedAt} IS NULL`), + .where(sql`${table.deletedAt} IS NULL AND ${table.projectId} IS NOT NULL`), userIdx: index('folder_user_idx').on(table.userId), workspaceResourceParentIdx: index('folder_workspace_resource_parent_idx').on( table.workspaceId, @@ -2503,10 +2495,8 @@ export const workspaceFiles = pgTable( 'workspace_files', { id: text('id').primaryKey(), - /** Canonical ownership, independent of purpose, creator, and secret source. NULL awaits repair. */ - // contract-pending(after entity-aware readers and audited backfill are deployed): require both ownership fields. - entityType: text('entity_type'), - entityId: text('entity_id'), + /** Direct owner, independent of creator, caller, and billing payer. */ + projectId: text('project_id').references(() => project.id, { onDelete: 'restrict' }), key: text('key').notNull(), /** Project creator references clear on account deletion; other file contexts retain their cascade. */ userId: text('user_id').references(() => user.id, { onDelete: 'cascade' }), @@ -2591,26 +2581,25 @@ export const workspaceFiles = pgTable( (table) => ({ creatorLifetimeCheck: check( 'workspace_files_creator_lifetime_check', - sql`${table.userId} IS NOT NULL OR coalesce(${table.entityType} = 'project' AND char_length(${table.originalCreatorUserId}) > 0, false)` + sql`${table.userId} IS NOT NULL OR coalesce(${table.projectId} IS NOT NULL AND char_length(${table.originalCreatorUserId}) > 0, false)` + ), + ownerCheck: check( + 'workspace_files_owner_check', + sql`num_nonnulls(${table.workspaceId}, ${table.projectId}, ${table.organizationId}) <= 1` ), - entityBindingCheck: check( - 'workspace_files_entity_binding_check', - sql`(${table.entityType} IS NULL AND ${table.entityId} IS NULL) OR (${table.entityType} IS NOT NULL AND ${table.entityId} IS NOT NULL AND ${table.entityType} IN ('workspace', 'project', 'organization', 'user') AND char_length(${table.entityId}) > 0)` + projectBindingCheck: check( + 'workspace_files_project_binding_check', + sql`(${table.projectId} IS NOT NULL) = (${table.context} = 'project') AND (${table.projectId} IS NULL OR ${table.chatId} IS NULL)` ), - entityIdIdx: index('workspace_files_entity_id_idx') - .on(table.entityType, table.entityId, table.id) + projectIdIdx: index('workspace_files_project_id_idx') + .on(table.projectId, table.id) .concurrently(), - entityFolderOriginalNameActiveUnique: uniqueIndex( - 'workspace_files_entity_folder_name_active_unique' + projectFolderOriginalNameActiveUnique: uniqueIndex( + 'workspace_files_project_folder_name_active_unique' ) - .on( - table.entityType, - table.entityId, - sql`coalesce(${table.folderId}, '')`, - table.originalName - ) + .on(table.projectId, sql`coalesce(${table.folderId}, '')`, table.originalName) .concurrently() - .where(sql`${table.deletedAt} IS NULL AND ${table.context} IN ('workspace', 'project')`), + .where(sql`${table.deletedAt} IS NULL AND ${table.projectId} IS NOT NULL`), keyActiveUniqueIdx: uniqueIndex('workspace_files_key_active_unique') .on(table.key) .where(sql`${table.deletedAt} IS NULL`), @@ -2635,12 +2624,12 @@ export const workspaceFiles = pgTable( */ ownerSearchKeysetIdx: index('workspace_files_search_owner_keyset_idx') .on( - sql`coalesce(${table.entityType}, 'workspace')`, - sql`coalesce(${table.entityId}, ${table.workspaceId})`, + sql`CASE WHEN ${table.projectId} IS NOT NULL THEN 'project' ELSE 'workspace' END`, + sql`coalesce(${table.projectId}, ${table.workspaceId})`, table.id ) .where( - sql`${table.deletedAt} IS NULL AND ((${table.context} = 'workspace' AND ${table.workspaceId} IS NOT NULL) OR (${table.context} = 'project' AND ${table.entityType} = 'project' AND ${table.entityId} IS NOT NULL))` + sql`${table.deletedAt} IS NULL AND ((${table.context} = 'workspace' AND ${table.workspaceId} IS NOT NULL) OR (${table.context} = 'project' AND ${table.projectId} IS NOT NULL))` ) .concurrently(), workspaceActiveKeysetIdx: index('workspace_files_workspace_active_keyset_idx') diff --git a/packages/db/script-migrations/0003_backfill_workspace_storage_usage.ts b/packages/db/script-migrations/0003_backfill_workspace_storage_usage.ts index a0e8128aceb..dad650798ec 100644 --- a/packages/db/script-migrations/0003_backfill_workspace_storage_usage.ts +++ b/packages/db/script-migrations/0003_backfill_workspace_storage_usage.ts @@ -227,8 +227,8 @@ export function createPostgresStorageReconciliationStore(sql: Sql): StorageRecon WHERE organization_id = ${organizationId} UNION ALL SELECT file.size_bytes AS bytes FROM workspace_files file - INNER JOIN project ON project.id = file.entity_id - WHERE file.entity_type = 'project' AND file.context = 'project' + INNER JOIN project ON project.id = file.project_id + WHERE file.context = 'project' AND project.organization_id = ${organizationId} ) retained ` @@ -274,8 +274,8 @@ export function createPostgresStorageReconciliationStore(sql: Sql): StorageRecon WHERE organization_id IS NULL AND billed_account_user_id = ${userId} UNION ALL SELECT file.size_bytes AS bytes FROM workspace_files file - INNER JOIN project ON project.id = file.entity_id - WHERE file.entity_type = 'project' AND file.context = 'project' + INNER JOIN project ON project.id = file.project_id + WHERE file.context = 'project' AND project.organization_id IS NULL AND project.owner_id = ${userId} ) retained ` diff --git a/packages/db/script-migrations/0030_backfill_workspace_file_entities.ts b/packages/db/script-migrations/0030_backfill_workspace_file_entities.ts deleted file mode 100644 index 84c7d6b6ada..00000000000 --- a/packages/db/script-migrations/0030_backfill_workspace_file_entities.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { ScriptMigration } from '@sim/db/script-migrations/types' -import { createLogger } from '@sim/logger' -import type { Sql } from 'postgres' - -const logger = createLogger('FileEntityBackfill') -const BATCH_SIZE = 500 -const MAX_REPAIR_SAMPLES = 10 - -interface UnresolvedContext { - context: string - count: number - sampleFileIds: string[] -} - -/** Populates only audited legacy owners without changing file identities, bytes, or revisions. */ -export async function backfillWorkspaceFileEntities(sql: Sql) { - let afterFileId = '' - let scanned = 0 - let backfilled = 0 - const unresolved = new Map() - - for (;;) { - const rows = await sql< - { id: string; context: string; mappedType: string | null; mappedId: string | null }[] - >` - SELECT file.id, file.context, owner.entity_type AS "mappedType", owner.entity_id AS "mappedId" - FROM workspace_files file - CROSS JOIN LATERAL workspace_file_legacy_entity( - file.context, file.workspace_id, file.organization_id, file.user_id - ) owner - WHERE file.entity_type IS NULL AND file.entity_id IS NULL AND file.id > ${afterFileId} - ORDER BY file.id - LIMIT ${BATCH_SIZE} - ` - if (!rows.length) break - afterFileId = rows[rows.length - 1].id - scanned += rows.length - - for (const row of rows) { - if (row.mappedType && row.mappedId) continue - let summary = unresolved.get(row.context) - if (!summary) { - summary = { context: row.context, count: 0, sampleFileIds: [] } - unresolved.set(row.context, summary) - } - summary.count += 1 - if (summary.sampleFileIds.length < MAX_REPAIR_SAMPLES) summary.sampleFileIds.push(row.id) - } - - const ids = rows.filter((row) => row.mappedType && row.mappedId).map((row) => row.id) - if (!ids.length) continue - const updated = await sql.begin(async (tx) => { - await tx`SET LOCAL lock_timeout = '5s'` - await tx`SET LOCAL statement_timeout = '30s'` - /** Lock in key order; the trigger derives from the current row after concurrent legacy writes. */ - await tx` - SELECT id FROM workspace_files WHERE id = ANY(${ids}::text[]) ORDER BY id FOR UPDATE - ` - return tx<{ id: string }[]>` - UPDATE workspace_files AS file SET entity_type = file.entity_type - WHERE file.id = ANY(${ids}::text[]) - AND file.entity_type IS NULL AND file.entity_id IS NULL - AND EXISTS ( - SELECT 1 FROM workspace_file_legacy_entity( - file.context, file.workspace_id, file.organization_id, file.user_id - ) owner WHERE owner.entity_type IS NOT NULL AND owner.entity_id IS NOT NULL - ) - RETURNING file.id - ` - }) - backfilled += updated.length - } - - return { scanned, backfilled, unresolved: [...unresolved.values()] } -} - -export const backfillWorkspaceFileEntitiesMigration: ScriptMigration = { - name: '0030_backfill_workspace_file_entities', - async up(sql) { - const result = await backfillWorkspaceFileEntities(sql) - logger.info('File entity ownership backfill completed', result) - if (result.unresolved.length) { - logger.warn('Unresolved legacy file owners remain unavailable to entity-scoped operations', { - unresolved: result.unresolved, - }) - } - }, -} diff --git a/packages/db/script-migrations/0031_backfill_file_folder_entities.ts b/packages/db/script-migrations/0031_backfill_file_folder_entities.ts deleted file mode 100644 index b059df7b81e..00000000000 --- a/packages/db/script-migrations/0031_backfill_file_folder_entities.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { ScriptMigration } from '@sim/db/script-migrations/types' -import { createLogger } from '@sim/logger' -import type { Sql } from 'postgres' - -const logger = createLogger('FileFolderEntityBackfill') -const BATCH_SIZE = 500 -const MAX_REPAIR_SAMPLES = 10 - -/** Backfills compatibility folder ownership; mismatched version scopes are reported without reassignment. */ -export async function backfillFileFolderEntities(sql: Sql) { - let afterFolderId = '' - let backfilled = 0 - for (;;) { - const rows = await sql<{ id: string }[]>` - SELECT id FROM folder - WHERE entity_type IS NULL AND entity_id IS NULL AND workspace_id IS NOT NULL - AND id > ${afterFolderId} - ORDER BY id LIMIT ${BATCH_SIZE} - ` - if (!rows.length) break - afterFolderId = rows[rows.length - 1].id - const ids = rows.map((row) => row.id) - const updated = await sql.begin(async (tx) => { - await tx`SET LOCAL lock_timeout = '5s'` - await tx`SET LOCAL statement_timeout = '30s'` - await tx`SELECT id FROM folder WHERE id = ANY(${ids}::text[]) ORDER BY id FOR UPDATE` - return tx<{ id: string }[]>` - UPDATE folder SET entity_type = entity_type - WHERE id = ANY(${ids}::text[]) AND entity_type IS NULL AND entity_id IS NULL - AND workspace_id IS NOT NULL - RETURNING id - ` - }) - backfilled += updated.length - } - - let afterVersionId = '' - let mismatchedVersions = 0 - const sampleVersionIds: string[] = [] - for (;;) { - const rows = await sql<{ id: string; compatible: boolean }[]>` - SELECT version.id, - coalesce( - (coalesce(file.entity_type, owner.entity_type) = 'workspace' AND file.context = 'workspace' - AND version.workspace_id IS NOT DISTINCT FROM coalesce(file.entity_id, owner.entity_id)) - OR (file.entity_type = 'project' AND file.context = 'project' AND version.workspace_id IS NULL), - false - ) AS compatible - FROM workspace_file_version version - LEFT JOIN workspace_files file ON file.id = version.file_id - LEFT JOIN LATERAL workspace_file_legacy_entity( - file.context, file.workspace_id, file.organization_id, file.user_id - ) owner ON true - WHERE version.id > ${afterVersionId} - ORDER BY version.id LIMIT ${BATCH_SIZE} - ` - if (!rows.length) break - afterVersionId = rows[rows.length - 1].id - for (const row of rows) { - if (row.compatible) continue - mismatchedVersions += 1 - if (sampleVersionIds.length < MAX_REPAIR_SAMPLES) sampleVersionIds.push(row.id) - } - } - - return { backfilled, mismatchedVersions, sampleVersionIds } -} - -export const backfillFileFolderEntitiesMigration: ScriptMigration = { - name: '0031_backfill_file_folder_entities', - async up(sql) { - const result = await backfillFileFolderEntities(sql) - logger.info('File folder entity backfill completed', result) - if (result.mismatchedVersions) { - throw new Error( - 'Retained file versions require ownership repair before continuing. Review the reported version IDs, repair their ownership explicitly, and rerun the migration.' - ) - } - }, -} diff --git a/packages/db/script-migrations/0031_validate_file_ownership.ts b/packages/db/script-migrations/0031_validate_file_ownership.ts new file mode 100644 index 00000000000..4ae9c38b55f --- /dev/null +++ b/packages/db/script-migrations/0031_validate_file_ownership.ts @@ -0,0 +1,62 @@ +import type { ScriptMigration } from '@sim/db/script-migrations/types' +import { createLogger } from '@sim/logger' +import type { Sql } from 'postgres' + +const logger = createLogger('FileOwnershipValidation') +const BATCH_SIZE = 500 +const MAX_REPAIR_SAMPLES = 10 + +/** Validates existing typed owners and retained version bindings without rewriting ownership. */ +export async function validateFileOwnership(sql: Sql) { + await sql.begin(async (tx) => { + await tx`SET LOCAL lock_timeout = '5s'` + await tx`ALTER TABLE workspace_files + VALIDATE CONSTRAINT workspace_files_project_id_project_id_fk, + VALIDATE CONSTRAINT workspace_files_owner_check, + VALIDATE CONSTRAINT workspace_files_project_binding_check` + await tx`ALTER TABLE folder + VALIDATE CONSTRAINT folder_project_id_project_id_fk, + VALIDATE CONSTRAINT folder_owner_check` + }) + + let afterVersionId = '' + let mismatchedVersions = 0 + const sampleVersionIds: string[] = [] + for (;;) { + const rows = await sql<{ id: string; compatible: boolean }[]>` + SELECT version.id, + coalesce( + (file.workspace_id IS NOT NULL AND file.project_id IS NULL AND file.organization_id IS NULL AND file.context = 'workspace' + AND version.workspace_id IS NOT DISTINCT FROM file.workspace_id) + OR (file.project_id IS NOT NULL AND file.context = 'project' AND version.workspace_id IS NULL), + false + ) AS compatible + FROM workspace_file_version version + LEFT JOIN workspace_files file ON file.id = version.file_id + WHERE version.id > ${afterVersionId} + ORDER BY version.id LIMIT ${BATCH_SIZE} + ` + if (!rows.length) break + afterVersionId = rows[rows.length - 1].id + for (const row of rows) { + if (row.compatible) continue + mismatchedVersions += 1 + if (sampleVersionIds.length < MAX_REPAIR_SAMPLES) sampleVersionIds.push(row.id) + } + } + + return { mismatchedVersions, sampleVersionIds } +} + +export const validateFileOwnershipMigration: ScriptMigration = { + name: '0031_validate_file_ownership', + async up(sql) { + const result = await validateFileOwnership(sql) + logger.info('File ownership validation completed', result) + if (result.mismatchedVersions) { + throw new Error( + 'Retained file versions require ownership repair before continuing. Review the reported version IDs, repair their ownership explicitly, and rerun the migration.' + ) + } + }, +} diff --git a/packages/db/script-migrations/0032_backfill_project_file_creators.ts b/packages/db/script-migrations/0032_backfill_project_file_creators.ts index d39ed6a18bd..949bcb03b9b 100644 --- a/packages/db/script-migrations/0032_backfill_project_file_creators.ts +++ b/packages/db/script-migrations/0032_backfill_project_file_creators.ts @@ -11,7 +11,7 @@ async function backfillTable(sql: Sql, table: 'workspace_files' | 'folder'): Pro for (;;) { const rows = await sql<{ id: string }[]>` SELECT id FROM ${sql(table)} - WHERE entity_type = 'project' AND original_creator_user_id IS NULL AND user_id IS NOT NULL + WHERE project_id IS NOT NULL AND original_creator_user_id IS NULL AND user_id IS NOT NULL AND id > ${afterId} ORDER BY id LIMIT ${BATCH_SIZE} ` @@ -24,7 +24,7 @@ async function backfillTable(sql: Sql, table: 'workspace_files' | 'folder'): Pro await tx`SELECT id FROM ${tx(table)} WHERE id = ANY(${ids}::text[]) ORDER BY id FOR UPDATE` return tx<{ id: string }[]>` UPDATE ${tx(table)} SET original_creator_user_id = user_id - WHERE id = ANY(${ids}::text[]) AND entity_type = 'project' + WHERE id = ANY(${ids}::text[]) AND project_id IS NOT NULL AND original_creator_user_id IS NULL AND user_id IS NOT NULL RETURNING id ` diff --git a/packages/db/script-migrations/0033_backfill_public_share_entities.ts b/packages/db/script-migrations/0033_backfill_public_share_entities.ts index c3453c76ccb..8a177e33b75 100644 --- a/packages/db/script-migrations/0033_backfill_public_share_entities.ts +++ b/packages/db/script-migrations/0033_backfill_public_share_entities.ts @@ -39,12 +39,12 @@ export async function backfillPublicShareEntities(sql: Sql) { AND share.workspace_id IS NOT NULL AND (share.resource_type <> 'file' OR EXISTS ( SELECT 1 FROM workspace_files file - CROSS JOIN LATERAL workspace_file_legacy_entity( - file.context, file.workspace_id, file.organization_id, file.user_id + CROSS JOIN LATERAL workspace_file_owner( + file.context, file.workspace_id, file.project_id, file.organization_id, file.user_id ) owner WHERE file.id = share.resource_id - AND coalesce(file.entity_type, owner.entity_type) = 'workspace' - AND coalesce(file.entity_id, owner.entity_id) = share.workspace_id + AND owner.entity_type = 'workspace' + AND owner.entity_id = share.workspace_id )) RETURNING share.id ` diff --git a/packages/db/script-migrations/index.ts b/packages/db/script-migrations/index.ts index a75564763f3..d510112c258 100644 --- a/packages/db/script-migrations/index.ts +++ b/packages/db/script-migrations/index.ts @@ -10,8 +10,7 @@ import { projectionAclSkipUnfilledMigration } from '@sim/db/script-migrations/00 import { knowledgeProjectionAsyncMigration } from '@sim/db/script-migrations/0024_knowledge_projection_async' import { scopeKeywordProjectionsMigration } from '@sim/db/script-migrations/0025_scope_keyword_projections' import { userTableSchemaForWriteMigration } from '@sim/db/script-migrations/0026_user_table_schema_for_write' -import { backfillWorkspaceFileEntitiesMigration } from '@sim/db/script-migrations/0030_backfill_workspace_file_entities' -import { backfillFileFolderEntitiesMigration } from '@sim/db/script-migrations/0031_backfill_file_folder_entities' +import { validateFileOwnershipMigration } from '@sim/db/script-migrations/0031_validate_file_ownership' import { backfillProjectFileCreatorsMigration } from '@sim/db/script-migrations/0032_backfill_project_file_creators' import { backfillPublicShareEntitiesMigration } from '@sim/db/script-migrations/0033_backfill_public_share_entities' import type { Sql } from 'postgres' @@ -66,8 +65,7 @@ export const scriptMigrations: readonly ScriptMigration[] = [ * Search retirement (0027–0029) is an operator-run maintenance command, not a deploy step: * run `packages/db/scripts/retire-indexed-search.ts --help` for usage. */ - backfillWorkspaceFileEntitiesMigration, - backfillFileFolderEntitiesMigration, + validateFileOwnershipMigration, backfillProjectFileCreatorsMigration, backfillPublicShareEntitiesMigration, ] diff --git a/packages/testing/src/mocks/schema-tables.generated.ts b/packages/testing/src/mocks/schema-tables.generated.ts index bd5533e8454..e07dbd9b98d 100644 --- a/packages/testing/src/mocks/schema-tables.generated.ts +++ b/packages/testing/src/mocks/schema-tables.generated.ts @@ -53,8 +53,7 @@ export const GENERATED_SCHEMA_TABLES = { verification: ['id', 'identifier', 'value', 'expiresAt', 'createdAt', 'updatedAt'], folder: [ 'id', - 'entityType', - 'entityId', + 'projectId', 'resourceType', 'name', 'userId', @@ -604,8 +603,7 @@ export const GENERATED_SCHEMA_TABLES = { ], workspaceFiles: [ 'id', - 'entityType', - 'entityId', + 'projectId', 'key', 'userId', 'originalCreatorUserId', From 5cc36e827f46b865666c70f4f29d4669bdd498fe Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Mon, 5 Oct 2026 17:39:14 -0700 Subject: [PATCH 025/113] fix(db): parenthesize the file owner search index expression --- packages/db/migrations/meta/0401_snapshot.json | 2 +- packages/db/schema.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/db/migrations/meta/0401_snapshot.json b/packages/db/migrations/meta/0401_snapshot.json index e4ae12fc80e..60abff63254 100644 --- a/packages/db/migrations/meta/0401_snapshot.json +++ b/packages/db/migrations/meta/0401_snapshot.json @@ -28775,7 +28775,7 @@ "name": "workspace_files_search_owner_keyset_idx", "columns": [ { - "expression": "CASE WHEN \"project_id\" IS NOT NULL THEN 'project' ELSE 'workspace' END", + "expression": "(CASE WHEN \"project_id\" IS NOT NULL THEN 'project' ELSE 'workspace' END)", "asc": true, "isExpression": true, "nulls": "last" diff --git a/packages/db/schema.ts b/packages/db/schema.ts index f6aa68676ed..afd53f06157 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2624,7 +2624,7 @@ export const workspaceFiles = pgTable( */ ownerSearchKeysetIdx: index('workspace_files_search_owner_keyset_idx') .on( - sql`CASE WHEN ${table.projectId} IS NOT NULL THEN 'project' ELSE 'workspace' END`, + sql`(CASE WHEN ${table.projectId} IS NOT NULL THEN 'project' ELSE 'workspace' END)`, sql`coalesce(${table.projectId}, ${table.workspaceId})`, table.id ) From ef5498a25f1893ae3cb91b02e48a3cccc8c59be5 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Mon, 5 Oct 2026 18:24:24 -0700 Subject: [PATCH 026/113] test(projects): update new workspace fixtures for enforcement --- .../mothership/chat/application/set-effort.integration.ts | 5 +++-- apps/sim/lib/webhooks/registration-store.integration.ts | 5 +++-- apps/sim/scripts/test-workflow-stop-after-e2e.ts | 5 +++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/sim/lib/mothership/chat/application/set-effort.integration.ts b/apps/sim/lib/mothership/chat/application/set-effort.integration.ts index 29a3b33e596..18e9195855a 100644 --- a/apps/sim/lib/mothership/chat/application/set-effort.integration.ts +++ b/apps/sim/lib/mothership/chat/application/set-effort.integration.ts @@ -1,6 +1,7 @@ /** Exercises a chat's explicit effort choice against real PostgreSQL rows. */ import { db } from '@sim/db' import { copilotChats, permissions, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createSessionPrincipal } from '@sim/testing/factories/principal.factory' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' @@ -46,7 +47,7 @@ beforeAll(async () => { updatedAt: now, })) ) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Chat effort fixture', ownerId, @@ -65,7 +66,7 @@ beforeAll(async () => { afterAll(async () => { await db.delete(copilotChats).where(eq(copilotChats.workspaceId, workspaceId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(inArray(user.id, [ownerId, outsiderId])) await db.$client.end() }) diff --git a/apps/sim/lib/webhooks/registration-store.integration.ts b/apps/sim/lib/webhooks/registration-store.integration.ts index d46fa413659..1d62346117c 100644 --- a/apps/sim/lib/webhooks/registration-store.integration.ts +++ b/apps/sim/lib/webhooks/registration-store.integration.ts @@ -12,6 +12,7 @@ import { workflowDeploymentVersion, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -58,7 +59,7 @@ beforeAll(async () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Registration Store', ownerId: owner, @@ -111,7 +112,7 @@ beforeAll(async () => { }) afterAll(async () => { - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, owner)) }) diff --git a/apps/sim/scripts/test-workflow-stop-after-e2e.ts b/apps/sim/scripts/test-workflow-stop-after-e2e.ts index c27ab483826..8e7f142652d 100644 --- a/apps/sim/scripts/test-workflow-stop-after-e2e.ts +++ b/apps/sim/scripts/test-workflow-stop-after-e2e.ts @@ -215,6 +215,10 @@ async function seed() { await tx`insert into user_stats (id, user_id) values (${generateId()}, ${ownerId})` await tx`insert into workspace (id, name, owner_id, billed_account_user_id) values (${workspaceId}, 'Stop-after fixture', ${ownerId}, ${ownerId})` + await tx`insert into project (id, name, owner_id) + values (${workspaceId}, 'Stop-after project', ${ownerId})` + await tx`insert into project_workspace (project_id, workspace_id) + values (${workspaceId}, ${workspaceId})` await tx`insert into permissions (id, user_id, entity_type, entity_id, permission_type) values (${generateId()}, ${ownerId}, 'workspace', ${workspaceId}, 'admin')` await tx`insert into api_key (id, user_id, name, key, key_hash, type) @@ -505,6 +509,7 @@ try { try { await sql.begin(async (tx) => { await tx`delete from workspace where id = ${workspaceId}` + await tx`delete from project where id = ${workspaceId}` await tx`delete from "user" where id = ${ownerId}` }) break From 43f2119c6bde0153372f911e8b850ed287317352 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 6 Oct 2026 11:12:47 -0700 Subject: [PATCH 027/113] fix(files): harden ownership lifecycle and realtime admission --- apps/realtime/src/access-revalidation.ts | 2 +- .../handlers/file-doc-project.integration.ts | 262 +++++++++++++++++- .../src/handlers/file-doc.redis-store.test.ts | 19 +- apps/realtime/src/handlers/file-doc.test.ts | 15 +- apps/realtime/src/handlers/file-doc.ts | 70 ++++- apps/realtime/src/middleware/permissions.ts | 10 +- apps/realtime/vitest.config.ts | 8 +- .../app/api/v2/uploads/[uploadId]/route.ts | 12 +- .../background/cleanup-soft-deletes.test.ts | 1 + apps/sim/background/cleanup-soft-deletes.ts | 12 +- apps/sim/lib/billing/cleanup-dispatcher.ts | 14 +- apps/sim/lib/core/outbox/service.ts | 11 +- .../private-purge.integration.ts | 16 +- apps/sim/lib/projects/files/prefix-cleanup.ts | 48 +++- apps/sim/lib/projects/files/purge.ts | 13 +- .../workspace/workspace-file-versions.ts | 3 +- .../sim/lib/uploads/documents/compile.test.ts | 87 +++++- apps/sim/lib/uploads/documents/compile.ts | 97 +++++-- apps/sim/lib/uploads/documents/index.ts | 1 + apps/sim/lib/uploads/documents/references.ts | 174 +++++++++++- .../lib/uploads/documents/servable.test.ts | 7 +- .../lib/workspace-files/search/dispatcher.ts | 10 +- .../search/owners.integration.ts | 138 ++++++++- apps/sim/lib/workspace-files/search/source.ts | 16 +- apps/sim/package.json | 1 + bun.lock | 1 + packages/auth/src/principal.ts | 2 +- .../0403_file_search_owner_scope.sql | 9 + packages/db/scripts/push.ts | 1 + .../db/scripts/reconcile-file-ownership.ts | 41 +++ 30 files changed, 962 insertions(+), 139 deletions(-) create mode 100644 packages/db/scripts/reconcile-file-ownership.ts diff --git a/apps/realtime/src/access-revalidation.ts b/apps/realtime/src/access-revalidation.ts index 29a03db6272..95fb65ff1db 100644 --- a/apps/realtime/src/access-revalidation.ts +++ b/apps/realtime/src/access-revalidation.ts @@ -118,7 +118,7 @@ function collectScanTargets(io: IRoomManager['io']): ScanTarget[] { for (const name of socket.rooms) { if (name === socket.id) continue const ref = parseRoomName(name) - if (!ref) continue + if (!ref || ref.id.includes(':')) continue targets.push({ room: ref, name, socket: authed, userId: authed.userId }) } } diff --git a/apps/realtime/src/handlers/file-doc-project.integration.ts b/apps/realtime/src/handlers/file-doc-project.integration.ts index 2d02c19a335..003ef7b9cc4 100644 --- a/apps/realtime/src/handlers/file-doc-project.integration.ts +++ b/apps/realtime/src/handlers/file-doc-project.integration.ts @@ -5,14 +5,18 @@ import { FILE_DOC_EVENTS, FILE_DOC_SCHEMA_VERSION, FILE_DOC_SEED, + type JoinFileDocError, + type JoinFileDocPayload, type JoinFileDocSuccess, } from '@sim/realtime-protocol/file-doc' +import { createDeferred } from '@sim/testing/helpers/deferred' import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { Server } from 'socket.io' import { io as connect, type Socket } from 'socket.io-client' -import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' import * as Y from 'yjs' +import { startAccessRevalidationSweep } from '@/access-revalidation' import { env } from '@/env' import { cleanupFileDocForSocket, @@ -20,7 +24,11 @@ import { setupWorkspaceFileDocHandlers, } from '@/handlers/file-doc' import type { AuthenticatedSocket } from '@/middleware/auth' -import { beginRoomPermissionRead, commitRoomPermission } from '@/middleware/permissions' +import { + beginRoomPermissionRead, + commitRoomPermission, + ROLE_REVALIDATION_TTL_MS, +} from '@/middleware/permissions' import { MemoryRoomManager } from '@/rooms' const projectId = generateId() @@ -41,6 +49,15 @@ let http: HttpServer let io: Server let socketUrl: string const capturedActors: string[] = [] +const accessGates = new Map< + string, + { + requests: number + entered: ReturnType> + release: ReturnType> + status: number + } +>() async function listen(server: HttpServer) { await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)) @@ -68,6 +85,15 @@ beforeAll(async () => { response.writeHead(404).end('{}') return } + const gate = accessGates.get(actor) + if (gate && request.url.endsWith('/access') && ++gate.requests > 1) { + gate.entered.resolve() + await gate.release.promise + if (gate.status !== 200) { + response.writeHead(gate.status).end('{}') + return + } + } const role = actors.get(actor) if (!role) { response.writeHead(404).end('{}') @@ -109,7 +135,7 @@ beforeAll(async () => { socketUrl = await listen(http) }) -async function join(actor: string, assertedProject = projectId) { +async function startJoin(actor: string, assertedProject = projectId) { const socket = connect(socketUrl, { transports: ['websocket'], auth: { actor } }) clients.push(socket) await new Promise((resolve) => socket.once('connect', resolve)) @@ -126,7 +152,12 @@ async function join(actor: string, assertedProject = projectId) { clientId, schemaVersion: FILE_DOC_SCHEMA_VERSION, }) - return { socket, joined: await joined } + return { socket, joined } +} + +async function join(actor: string, assertedProject = projectId) { + const pending = await startJoin(actor, assertedProject) + return { socket: pending.socket, joined: await pending.joined } } function check(name: string, run: () => Promise) { @@ -171,6 +202,184 @@ describe('Project documents across the Socket.IO and internal HTTP boundary', () write.destroy() }) + for (const status of [200, 403, 503]) { + check(`withholds Project broadcasts while final access resolves to ${status}`, async () => { + const actor = `pending-${generateId()}` + actors.set(actor, 'read') + const gate = { + requests: 0, + entered: createDeferred(), + release: createDeferred(), + status, + } + accessGates.set(actor, gate) + const writer = await join('writer') + const installed = createDeferred() + const releaseAdapter = createDeferred() + const adapter = io.of('/').adapter + const addAll = adapter.addAll.bind(adapter) + adapter.addAll = async (id, rooms) => { + await addAll(id, rooms) + if ( + io.sockets.sockets.get(id)?.handshake.auth.actor === actor && + rooms.has(`project-file-doc:${projectId}/${fileId}`) + ) { + installed.resolve() + await releaseAdapter.promise + } + } + const pending = await startJoin(actor) + const outcome = pending.joined.then( + (value) => ({ value, error: null }), + (error: Error) => ({ value: null, error }) + ) + const frames: string[] = [] + pending.socket.on(FILE_DOC_EVENTS.MESSAGE, () => frames.push('document')) + pending.socket.on(FILE_DOC_EVENTS.PRESENCE, () => frames.push('presence')) + try { + await installed.promise + const edit = new Y.Doc() + Y.applyUpdate(edit, Buffer.from(seed, 'base64')) + edit.getText('body').insert(0, `while-pending-${status}`) + expect( + await writer.socket.timeout(1500).emitWithAck(FILE_DOC_EVENTS.UPDATE, { + fileId, + projectId, + docId, + updateId: generateId(), + update: Y.encodeStateAsUpdate(edit), + }) + ).toMatchObject({ status: 'accepted' }) + edit.destroy() + await join('reader') + const drained = new Promise((resolve) => + pending.socket.once('fixture-drained', resolve) + ) + io.sockets.sockets.get(pending.socket.id ?? '')?.emit('fixture-drained') + await drained + expect(frames).toEqual([]) + releaseAdapter.resolve() + await gate.entered.promise + await join('reader') + const finalDrain = new Promise((resolve) => + pending.socket.once('fixture-drained', resolve) + ) + io.sockets.sockets.get(pending.socket.id ?? '')?.emit('fixture-drained') + await finalDrain + expect(frames).toEqual([]) + } finally { + adapter.addAll = addAll + releaseAdapter.resolve() + gate.release.resolve() + } + const result = await outcome + if (status === 200) expect(result.value?.canWrite).toBe(false) + else { + expect(result.error).not.toBeNull() + expect( + io.sockets.sockets + .get(pending.socket.id ?? '') + ?.rooms.has(`project-file-doc:${projectId}/${fileId}`) + ).toBe(false) + expect(frames).toEqual([]) + } + accessGates.delete(actor) + pending.socket.disconnect() + writer.socket.disconnect() + }) + } + + for (const rejectLeave of [false, true]) { + check( + `a stale rejoin preserves the admitted document when delayed cleanup ${rejectLeave ? 'rejects' : 'resolves'}`, + async () => { + const actor = `rejoining-${generateId()}` + actors.set(actor, 'write') + const admitted = await join(actor) + const server = io.sockets.sockets.get(admitted.socket.id ?? '') + if (!server) throw new Error('Admitted socket missing') + const originalJoin = server.listeners(FILE_DOC_EVENTS.JOIN)[0] + if (!originalJoin) throw new Error('Document join handler missing') + const doc = new Y.Doc() + const clientId = doc.clientID + doc.destroy() + const done = createDeferred() + const entered = createDeferred() + const release = createDeferred() + const observeJoin = async (payload: JoinFileDocPayload) => { + try { + await originalJoin.call(server, payload) + } finally { + if (payload.clientId === clientId) done.resolve() + } + } + server.off(FILE_DOC_EVENTS.JOIN, originalJoin) + server.on(FILE_DOC_EVENTS.JOIN, observeJoin) + const adapter = io.of('/').adapter + const del = adapter.del.bind(adapter) + let held = false + adapter.del = async (id, name) => { + await del(id, name) + if ( + !held && + id === server.id && + name === `project-file-doc:${projectId}/${fileId}:pending` + ) { + held = true + entered.resolve() + await release.promise + if (rejectLeave) throw new Error('Fixture membership removal failed') + } + } + try { + admitted.socket.emit(FILE_DOC_EVENTS.JOIN, { + fileId, + projectId, + clientId, + schemaVersion: FILE_DOC_SCHEMA_VERSION, + }) + await entered.promise + const missingFileId = generateId() + const rejected = new Promise((resolve) => { + const onError = (error: JoinFileDocError) => { + if (error.fileId !== missingFileId) return + admitted.socket.off(FILE_DOC_EVENTS.JOIN_ERROR, onError) + resolve(error) + } + admitted.socket.on(FILE_DOC_EVENTS.JOIN_ERROR, onError) + }) + admitted.socket.emit(FILE_DOC_EVENTS.JOIN, { + fileId: missingFileId, + projectId, + clientId: clientId + 1, + schemaVersion: FILE_DOC_SCHEMA_VERSION, + }) + expect((await rejected).code).toBe('NOT_FOUND') + release.resolve() + await done.promise + const edit = new Y.Doc() + Y.applyUpdate(edit, Buffer.from(seed, 'base64')) + edit.getText('body').insert(0, 'preserved after failed navigation') + const reply = await admitted.socket.timeout(1500).emitWithAck(FILE_DOC_EVENTS.UPDATE, { + fileId, + projectId, + docId, + updateId: generateId(), + update: Y.encodeStateAsUpdate(edit), + }) + edit.destroy() + expect(reply).toMatchObject({ status: 'accepted' }) + } finally { + release.resolve() + adapter.del = del + server.off(FILE_DOC_EVENTS.JOIN, observeJoin) + server.on(FILE_DOC_EVENTS.JOIN, originalJoin) + admitted.socket.disconnect() + } + } + ) + } + check( 'a writer can publish and a reader joining later never replaces the actual editor', async () => { @@ -193,6 +402,51 @@ describe('Project documents across the Socket.IO and internal HTTP boundary', () } ) + check( + 'callback outages preserve membership and acknowledge writes as retryable; revocations still evict', + async () => { + const actor = `transient-${generateId()}` + actors.set(actor, 'write') + const writer = await join(actor) + const gate = { + requests: 1, + entered: createDeferred(), + release: createDeferred(), + status: 503, + } + gate.release.resolve() + accessGates.set(actor, gate) + const now = Date.now() + const clock = vi.spyOn(Date, 'now').mockReturnValue(now + ROLE_REVALIDATION_TTL_MS + 1) + const sweep = startAccessRevalidationSweep(new MemoryRoomManager(io)) + sweep.stop() + const room = `project-file-doc:${projectId}/${fileId}` + try { + await sweep.runOnce() + expect(io.sockets.sockets.get(writer.socket.id ?? '')?.rooms.has(room)).toBe(true) + const reply = await writer.socket.timeout(1500).emitWithAck(FILE_DOC_EVENTS.UPDATE, { + fileId, + projectId, + docId, + updateId: generateId(), + update: Y.encodeStateAsUpdate(document), + }) + expect(reply).toMatchObject({ + status: 'rejected', + code: 'TEMPORARY_FAILURE', + retryable: true, + }) + gate.status = 404 + await sweep.runOnce() + expect(io.sockets.sockets.get(writer.socket.id ?? '')?.rooms.has(room)).toBe(false) + } finally { + clock.mockRestore() + accessGates.delete(actor) + writer.socket.disconnect() + } + } + ) + check('a downgraded writer remains a reader but cannot submit another update', async () => { const writer = await join('writer') actors.set('writer', 'read') diff --git a/apps/realtime/src/handlers/file-doc.redis-store.test.ts b/apps/realtime/src/handlers/file-doc.redis-store.test.ts index a390ad43697..be7aa2c48b7 100644 --- a/apps/realtime/src/handlers/file-doc.redis-store.test.ts +++ b/apps/realtime/src/handlers/file-doc.redis-store.test.ts @@ -134,18 +134,21 @@ interface FakeSocket { * Recording the emits without routing them would hide the very thing these tests are about. */ function createIo(sockets: FakeSocket[]) { - const emitTo = (target: string, except: string | null, event: string, payload: unknown) => { + const emitTo = (target: string, except: string[], event: string, payload: unknown) => { for (const socket of sockets) { - if (socket.id === except || !socket.rooms.has(target)) continue + if ( + except.some((room) => room === socket.id || socket.rooms.has(room)) || + !socket.rooms.has(target) + ) + continue socket.emit(event, payload) } } - const to = vi.fn((target: string) => ({ - except: (exclude: string) => ({ - emit: (event: string, payload: unknown) => emitTo(target, exclude, event, payload), - }), - emit: (event: string, payload: unknown) => emitTo(target, null, event, payload), - })) + const channel = (target: string, excluded: string[] = []) => ({ + except: (exclude: string) => channel(target, [...excluded, exclude]), + emit: (event: string, payload: unknown) => emitTo(target, excluded, event, payload), + }) + const to = vi.fn((target: string) => channel(target)) return { to, in: vi.fn(() => ({ socketsLeave: () => {} })), diff --git a/apps/realtime/src/handlers/file-doc.test.ts b/apps/realtime/src/handlers/file-doc.test.ts index cda4e115c88..c7510db74f0 100644 --- a/apps/realtime/src/handlers/file-doc.test.ts +++ b/apps/realtime/src/handlers/file-doc.test.ts @@ -55,7 +55,7 @@ const ROOM_NAME = 'workspace-file-doc:file-1' interface SentMessage { target: string - except?: string + except?: string[] event: string payload: unknown } @@ -69,12 +69,11 @@ function createIo(deliver?: (message: SentMessage) => void) { } /** Records `io.in(socketId).socketsLeave(room)` — a socket forced out of a room from outside. */ const left: { socketId: string; room: string }[] = [] - const to = vi.fn((target: string) => ({ - except: (exclude: string) => ({ - emit: (event: string, payload: unknown) => emit({ target, except: exclude, event, payload }), - }), - emit: (event: string, payload: unknown) => emit({ target, event, payload }), - })) + const channel = (target: string, excluded: string[] = []) => ({ + except: (exclude: string) => channel(target, [...excluded, exclude]), + emit: (event: string, payload: unknown) => emit({ target, except: excluded, event, payload }), + }) + const to = vi.fn((target: string) => channel(target)) const inFn = vi.fn((socketId: string) => ({ socketsLeave: (room: string) => { left.push({ socketId, room }) @@ -647,7 +646,7 @@ describe('setupWorkspaceFileDocHandlers', () => { // the stream live (the emitting provider no-ops on its own echo). const fanout = sent .slice(before) - .filter((m) => m.event === FILE_DOC_EVENTS.MESSAGE && m.except === undefined) + .filter((m) => m.event === FILE_DOC_EVENTS.MESSAGE && !m.except?.includes('socket-1')) expect(fanout.length).toBeGreaterThan(0) // ...but it must NOT mark the doc dirty: a last-disconnect flush never persists agent content (the diff --git a/apps/realtime/src/handlers/file-doc.ts b/apps/realtime/src/handlers/file-doc.ts index c8aefc15932..7cde577fd12 100644 --- a/apps/realtime/src/handlers/file-doc.ts +++ b/apps/realtime/src/handlers/file-doc.ts @@ -320,7 +320,7 @@ function hasOversizedLegacyUpdate(bytes: Uint8Array): boolean { */ function broadcast(io: Server, name: string, payload: Uint8Array, exceptSocketId: string | null) { const channel = exceptSocketId ? io.to(name).except(exceptSocketId) : io.to(name) - channel.emit(FILE_DOC_EVENTS.MESSAGE, payload) + channel.except(`${name}:pending`).emit(FILE_DOC_EVENTS.MESSAGE, payload) } /** @@ -336,7 +336,7 @@ function broadcastLocal( exceptSocketId: string | null ) { const channel = exceptSocketId ? io.local.to(name).except(exceptSocketId) : io.local.to(name) - channel.emit(FILE_DOC_EVENTS.MESSAGE, payload) + channel.except(`${name}:pending`).emit(FILE_DOC_EVENTS.MESSAGE, payload) } /** @@ -555,7 +555,9 @@ function broadcastFileDocPresence(io: Server, name: string, room: FileDocRoom) { avatarUrl: owner.avatarUrl, }) } - io.to(name).emit(FILE_DOC_EVENTS.PRESENCE, { fileId: room.fileId, users }) + io.to(name) + .except(`${name}:pending`) + .emit(FILE_DOC_EVENTS.PRESENCE, { fileId: room.fileId, users }) } /** Whether the client has recorded that it seeded the document's initial content. */ @@ -1372,8 +1374,15 @@ async function handleClientUpdate( reject('NOT_JOINED', true, candidate.updateId) return } - const admission = isFileDocWriteAllowed(socket, io, name) - const allowed = typeof admission === 'boolean' ? admission : await admission + let allowed: boolean + try { + const admission = isFileDocWriteAllowed(socket, io, name) + allowed = typeof admission === 'boolean' ? admission : await admission + } catch (error) { + logger.warn('Document write authorization unavailable', { error }) + reject('TEMPORARY_FAILURE', true, candidate.updateId) + return + } if (!allowed) { reject('ACCESS_REVOKED', false, candidate.updateId) return @@ -1505,6 +1514,19 @@ export function setupWorkspaceFileDocHandlers( /** Co-mounted providers share invalidation membership until their last admission settles. */ const pendingMemberships = new Map() + let membershipTail = Promise.resolve() + function changeMembership(change: () => void | Promise): Promise { + const pending = membershipTail.then(change) + membershipTail = pending.catch(() => {}) + return pending + } + function leaveUnclaimedRoom(name: string): Promise { + return changeMembership(() => { + if (socketToRoomName.get(socket.id) !== name && !pendingMemberships.has(name)) + return socket.leave(name) + }) + } + socket.on(FILE_DOC_EVENTS.JOIN, async (payload: JoinFileDocPayload) => { const { fileId, clientId, projectId } = payload // Hoisted so the catch can tell whether this join was superseded (a switch to another file) @@ -1693,7 +1715,9 @@ export function setupWorkspaceFileDocHandlers( */ pendingMemberships.set(name, (pendingMemberships.get(name) ?? 0) + 1) subscribed = true - await socket.join(admissionName) + await changeMembership(() => socket.join(admissionName)) + if (socketToRoomName.get(socket.id) !== name) + await changeMembership(() => socket.join(`${name}:pending`)) const joinedVersion = Math.max(entry.syncedVersion ?? 0, (await store.getSyncedVersion(name)) ?? 0) || undefined /** Adapter membership can wait; resolve access again before checking the final generation. */ @@ -1721,7 +1745,7 @@ export function setupWorkspaceFileDocHandlers( return } if (!canRegisterJoin()) return - await socket.join(name) + await changeMembership(() => socket.join(name)) if (projectId) { const currentAccess = await authorizeJoin() if (!currentAccess) return @@ -1785,8 +1809,9 @@ export function setupWorkspaceFileDocHandlers( // and simply re-runs the sync handshake, idempotently. const currentName = socketToRoomName.get(socket.id) if (currentName && currentName !== name) { - socket.leave(currentName) cleanupFileDocForSocket(socket.id, io) + await leaveUnclaimedRoom(currentName) + if (!canRegisterJoin()) return } // ADD this provider's clientID to the socket's ownership set (do NOT overwrite a sibling @@ -1803,6 +1828,14 @@ export function setupWorkspaceFileDocHandlers( clientMap.set(clientId, { clientId, userId, userName, avatarUrl }) socketToRoomName.set(socket.id, name) registered = true + await changeMembership(() => socket.leave(`${name}:pending`)) + if (!canRegisterJoin()) { + if (isCurrentJoin() && socketToRoomName.get(socket.id) === name) { + cleanupFileDocForSocket(socket.id, io) + await leaveUnclaimedRoom(name) + } + return + } // Attribution for the server-side persist, refreshed to the actual editor on each edit in // `handleMessage`. @@ -1855,8 +1888,13 @@ export function setupWorkspaceFileDocHandlers( if (remaining > 0) pendingMemberships.set(name, remaining) else { pendingMemberships.delete(name) - await socket.leave(admissionName) - if (socketToRoomName.get(socket.id) !== name) await socket.leave(name) + await changeMembership(async () => { + if (pendingMemberships.has(name)) return + if (socketToRoomName.get(socket.id) !== name) await socket.leave(name) + if (pendingMemberships.has(name)) return + await socket.leave(`${name}:pending`) + await socket.leave(admissionName) + }) } } } @@ -1868,9 +1906,13 @@ export function setupWorkspaceFileDocHandlers( * Roll back ownership only if this attempt committed it. A failed provisional admission must * preserve a previous file's binding and any co-mounted provider already in the target room. */ - if (registered && socketToRoomName.get(socket.id) === name) { - socket.leave(name) + if ( + registered && + joinGeneration.get(socket.id) === generation && + socketToRoomName.get(socket.id) === name + ) { cleanupFileDocForSocket(socket.id, io) + await leaveUnclaimedRoom(name) } destroyRoomIfIdle(name) } catch {} @@ -1904,7 +1946,7 @@ export function setupWorkspaceFileDocHandlers( } ) - socket.on(FILE_DOC_EVENTS.LEAVE, (payload?: LeaveFileDocPayload) => { + socket.on(FILE_DOC_EVENTS.LEAVE, async (payload?: LeaveFileDocPayload) => { try { // Cancel an in-flight join whose file the client is now leaving (or an unscoped leave): a // join still awaiting authorization would otherwise complete after the client left, register @@ -1923,8 +1965,8 @@ export function setupWorkspaceFileDocHandlers( // prior document must not evict the socket from one it has since opened. if (payload?.fileId && roomName(fileDocRoom(payload.fileId, payload.projectId)) !== name) return - socket.leave(name) cleanupFileDocForSocket(socket.id, io) + await leaveUnclaimedRoom(name) } catch (error) { logger.error('Error leaving file-doc room:', error) } diff --git a/apps/realtime/src/middleware/permissions.ts b/apps/realtime/src/middleware/permissions.ts index 8581a99d3db..ab2c2cca569 100644 --- a/apps/realtime/src/middleware/permissions.ts +++ b/apps/realtime/src/middleware/permissions.ts @@ -265,15 +265,12 @@ async function resolveRoleUncached( // this one — but returned after it — bury this result. return commitRoleDecision(key, role, readSeq) } catch (error) { - logger.warn( - `Failed to re-validate role for user ${userId} on ${roomName(room)}; using last known role`, - error - ) + logger.warn(`Failed to re-validate role for user ${userId} on ${roomName(room)}`, error) // Prefer the last recorded decision — even if expired, and even if it is `null` for an // already-revoked user — so a recorded revocation survives a transient DB failure // instead of reverting to the stale join-time role. Only trust `fallbackRole` when // nothing has been recorded for this (user, workflow) yet. - if (room.type === ROOM_TYPES.PROJECT_FILE_DOC) return null + if (room.type === ROOM_TYPES.PROJECT_FILE_DOC) throw error const lastKnown = roleCache.get(key) return lastKnown !== undefined ? lastKnown.role : fallbackRole } @@ -286,7 +283,8 @@ async function resolveRoleUncached( * (single-flight), so out-of-order cache writes cannot resurrect revoked access. * * Returns `null` when the user genuinely has no access (removed/revoked, or the room's - * resource is gone). On a transient DB failure it reuses the last recorded decision for + * resource is gone). Project callback failures throw so callers can retry without recording a revocation. + * On a transient DB failure for other room types, it reuses the last recorded decision for * this (user, room) — including a previously recorded revocation (`null`) — and only * falls back to `fallbackRole` when no decision has been recorded yet, so a blip neither * blocks legitimate editors nor resurrects already-revoked access. diff --git a/apps/realtime/vitest.config.ts b/apps/realtime/vitest.config.ts index 870fb3eaf0a..6771642c3e5 100644 --- a/apps/realtime/vitest.config.ts +++ b/apps/realtime/vitest.config.ts @@ -1,10 +1,10 @@ import path from 'node:path' import { defineConfig, mergeConfig } from 'vitest/config' -import sharedConfig from '../../vitest.shared' +import sharedConfig, { integrationTestConfig } from '../../vitest.shared' export default defineConfig(({ mode }) => mergeConfig( - sharedConfig, + mergeConfig(sharedConfig, mode === 'integration' ? integrationTestConfig : {}), defineConfig({ test: { include: mode === 'integration' ? ['src/**/*.integration.ts'] : ['**/*.test.{ts,tsx}'], @@ -16,10 +16,6 @@ export default defineConfig(({ mode }) => ...(mode === 'integration' ? { fileParallelism: false, - reporters: ['default', 'json'], - outputFile: { - json: process.env.INTEGRATION_REPORT_PATH ?? 'test-results/integration.json', - }, } : {}), }, diff --git a/apps/sim/app/api/v2/uploads/[uploadId]/route.ts b/apps/sim/app/api/v2/uploads/[uploadId]/route.ts index 0a2ec6ccebf..42ddd56e355 100644 --- a/apps/sim/app/api/v2/uploads/[uploadId]/route.ts +++ b/apps/sim/app/api/v2/uploads/[uploadId]/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { localPutUploadContract } from '@/lib/api/contracts/upload-sessions' import { parseRequest } from '@/lib/api/server' @@ -11,6 +12,8 @@ import { } from '@/lib/uploads/upload-session/service' import { v2Error, v2HttpError, v2UploadDataPlaneError } from '@/app/api/v2/lib/response' +const logger = createLogger('LocalUpload') + interface LocalPutRouteParams { params: Promise<{ uploadId: string }> } @@ -79,7 +82,14 @@ export const PUT = withRouteHandler( metadata: uploadSessionObjectMetadata(session), }) } catch (error) { - await queueRetiredProjectUploadCleanup(session) + try { + await queueRetiredProjectUploadCleanup(session) + } catch (cleanupError) { + logger.error( + 'Could not schedule retired upload cleanup; retirement reconciliation will retry', + { uploadId: session.id, error: cleanupError } + ) + } if (error instanceof LocalUploadBodyError) { return v2Error('BAD_REQUEST', error.message) } diff --git a/apps/sim/background/cleanup-soft-deletes.test.ts b/apps/sim/background/cleanup-soft-deletes.test.ts index b49d4c91684..08271563f8f 100644 --- a/apps/sim/background/cleanup-soft-deletes.test.ts +++ b/apps/sim/background/cleanup-soft-deletes.test.ts @@ -54,6 +54,7 @@ vi.mock('@/lib/cleanup/batch-delete', () => ({ chunkedBatchDelete: mockChunkedBatchDelete, chunkedBatchDeleteByScope: mockScopedChunkedBatchDelete, DEFAULT_DELETE_CHUNK_SIZE: 1000, + DEFAULT_MAX_BATCHES_PER_TABLE: 50, deleteRowsById: mockDeleteRowsById, selectRowsByIdChunks: mockSelectRowsByIdChunks, })) diff --git a/apps/sim/background/cleanup-soft-deletes.ts b/apps/sim/background/cleanup-soft-deletes.ts index e1e8b82c402..c8e359fd819 100644 --- a/apps/sim/background/cleanup-soft-deletes.ts +++ b/apps/sim/background/cleanup-soft-deletes.ts @@ -28,6 +28,7 @@ import { chunkedBatchDeleteByScope, consumeRowBudget, DEFAULT_DELETE_CHUNK_SIZE, + DEFAULT_MAX_BATCHES_PER_TABLE, type RowBudget, selectRowsByIdChunks, } from '@/lib/cleanup/batch-delete' @@ -849,9 +850,16 @@ export async function runCleanupSoftDeletes( const startTime = Date.now() const { workspaceIds, retentionHours, label } = payload const scope = resolveCleanupOwnerScope(payload) + const projectFileBudget = budgets?.files ?? { + remaining: DEFAULT_DELETE_CHUNK_SIZE * DEFAULT_MAX_BATCHES_PER_TABLE, + } + const projectFolderBudget = budgets?.folders ?? { + remaining: DEFAULT_DELETE_CHUNK_SIZE * DEFAULT_MAX_BATCHES_PER_TABLE, + } for (const projectId of [...new Set(payload.projectIds ?? [])].sort()) { - await cleanupArchivedProjectFiles(projectId, budgets?.files) - await cleanupArchivedProjectFileFolders(projectId, budgets?.folders) + if (projectFileBudget.remaining <= 0 && projectFolderBudget.remaining <= 0) break + await cleanupArchivedProjectFiles(projectId, projectFileBudget) + await cleanupArchivedProjectFileFolders(projectId, projectFolderBudget) } if (scope.ids.length === 0) { diff --git a/apps/sim/lib/billing/cleanup-dispatcher.ts b/apps/sim/lib/billing/cleanup-dispatcher.ts index 1577e633acb..08a56cb0361 100644 --- a/apps/sim/lib/billing/cleanup-dispatcher.ts +++ b/apps/sim/lib/billing/cleanup-dispatcher.ts @@ -383,11 +383,15 @@ async function forEachCleanupChunk( } } for (const group of groups.values()) { - await emitChunk({ - ...group, - workspaceIds: [], - label: `${group.plan}/projects/${group.projectIds[0]}`, - }) + for (const projectId of group.projectIds) { + if (shouldStop()) break + await emitChunk({ + ...group, + projectIds: [projectId], + workspaceIds: [], + label: `${group.plan}/projects/${projectId}`, + }) + } } } } diff --git a/apps/sim/lib/core/outbox/service.ts b/apps/sim/lib/core/outbox/service.ts index 54ce6e779f4..2467bc5a2b5 100644 --- a/apps/sim/lib/core/outbox/service.ts +++ b/apps/sim/lib/core/outbox/service.ts @@ -85,9 +85,13 @@ export interface DeferredOutboxHandlerResult { * budget. False is reserved for waits on an internal dependency whose own * outbox row independently reaches completed or dead-letter, and for * bounded continuation after durable progress (`continueOutboxHandler`), - * or external polling with a separately persisted, finite poll allowance. + * external polling with a separately persisted, finite poll allowance, or a + * retained storage tombstone whose successful bounded sweeps must recur because + * an empty listing cannot prove that in-flight provider writes have finished. */ consumeAttempt?: boolean + /** Reset consecutive failures only after a complete successful recurring sweep. */ + resetAttempts?: boolean } export function deferOutboxHandler( @@ -822,7 +826,10 @@ async function scheduleDeferred( event: typeof outboxEvent.$inferSelect, result: DeferredOutboxHandlerResult ): Promise<'pending' | 'dead_letter' | 'lease_lost'> { - const nextAttempts = event.attempts + (result.consumeAttempt === false ? 0 : 1) + const nextAttempts = + result.resetAttempts && result.consumeAttempt === false + ? 0 + : event.attempts + (result.consumeAttempt === false ? 0 : 1) if (result.consumeAttempt !== false && nextAttempts >= event.maxAttempts) { const updated = await updateIfLeaseHeld(event, { attempts: nextAttempts, diff --git a/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts b/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts index 6ec9fa7920a..99a5b46b388 100644 --- a/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts +++ b/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts @@ -442,11 +442,25 @@ describe('Private Project teardown and durable object cleanup', () => { .set({ availableAt: new Date(0) }) .where(eq(outboxEvent.id, final.id)) expect(await processOutboxEventById(final.id, projectFilePrefixCleanupOutboxHandlers)).toBe( - 'completed' + 'pending' + ) + await expect(readFile(join(storageRoot, upload.finalKey))).rejects.toMatchObject({ + code: 'ENOENT', + }) + await writeFile(join(storageRoot, upload.finalKey), 'finished-after-empty-sweep') + await db + .update(outboxEvent) + .set({ availableAt: new Date(0), attempts: 4 }) + .where(eq(outboxEvent.id, final.id)) + expect(await processOutboxEventById(final.id, projectFilePrefixCleanupOutboxHandlers)).toBe( + 'pending' ) await expect(readFile(join(storageRoot, upload.finalKey))).rejects.toMatchObject({ code: 'ENOENT', }) + const [retained] = await db.select().from(outboxEvent).where(eq(outboxEvent.id, final.id)) + expect(retained.attempts).toBe(0) + expect(retained.availableAt.getTime()).toBeGreaterThan(Date.now()) const unsafe = generateId() await db.insert(outboxEvent).values({ id: unsafe, diff --git a/apps/sim/lib/projects/files/prefix-cleanup.ts b/apps/sim/lib/projects/files/prefix-cleanup.ts index 30ba90a870f..cc1175b03fe 100644 --- a/apps/sim/lib/projects/files/prefix-cleanup.ts +++ b/apps/sim/lib/projects/files/prefix-cleanup.ts @@ -7,6 +7,7 @@ import { isRecordLike } from '@sim/utils/object' import { and, asc, eq, gt, inArray, isNull, sql } from 'drizzle-orm' import { continueOutboxHandler, + deferOutboxHandler, enqueueOutboxEvent, type OutboxHandler, type OutboxHandlerRegistry, @@ -28,6 +29,7 @@ import { PROJECT_FILE_UPLOAD_BINDING_KEY } from '@/lib/uploads/upload-session/ty const EVENT_TYPE = 'project-file.storage.prefix-cleanup' const PAGE_SIZE = 500 const CLOCK_SKEW_MS = 60_000 +const RETIRED_PREFIX_SWEEP_INTERVAL_MS = 24 * 60 * 60 * 1000 type UploadCleanup = Pick< UploadSessionRecord, @@ -36,9 +38,11 @@ type UploadCleanup = Pick< interface CleanupPayload { projectId: string uploads?: UploadCleanup[] + retained?: boolean + uploadCursor?: number } -/** No transfer can be exposed after fresh admission fails, so retirement bounds its last signature. */ +/** Expired signatures stop new transfers, but a transfer already in progress may finish later. */ function projectUploadCleanupAvailableAt(now = new Date()): Date { return new Date(now.getTime() + UPLOAD_URL_TTL_MS + CLOCK_SKEW_MS) } @@ -52,7 +56,16 @@ function parsePayload(value: unknown): CleanupPayload { if (!isRecordLike(value) || typeof value.projectId !== 'string') throw new Error('Invalid Project cleanup payload') const prefix = prefixFor(value.projectId) - if (value.uploads === undefined) return { projectId: value.projectId } + const retained = value.retained === true + const uploadCursor = value.uploadCursor === undefined ? 0 : value.uploadCursor + if ( + !Number.isSafeInteger(uploadCursor) || + typeof uploadCursor !== 'number' || + uploadCursor < 0 || + uploadCursor > PAGE_SIZE + ) + throw new Error('Invalid retired upload cursor') + if (value.uploads === undefined) return { projectId: value.projectId, retained, uploadCursor } if (!Array.isArray(value.uploads) || value.uploads.length > PAGE_SIZE) throw new Error('Invalid upload cleanup batch') const uploads = value.uploads.map((upload): UploadCleanup => { @@ -78,7 +91,7 @@ function parsePayload(value: unknown): CleanupPayload { finalKey: upload.finalKey, } }) - return { projectId: value.projectId, uploads } + return { projectId: value.projectId, uploads, retained, uploadCursor } } async function enqueueSweeps( @@ -87,9 +100,14 @@ async function enqueueSweeps( retiredAt = new Date() ) { await enqueueOutboxEvent(executor, EVENT_TYPE, payload) - await enqueueOutboxEvent(executor, EVENT_TYPE, payload, { - availableAt: projectUploadCleanupAvailableAt(retiredAt), - }) + await enqueueOutboxEvent( + executor, + EVENT_TYPE, + { ...payload, retained: true }, + { + availableAt: projectUploadCleanupAvailableAt(retiredAt), + } + ) } /** Must follow Project and payer locks and precede directory/file locks in the retirement transaction. */ @@ -266,7 +284,8 @@ const cleanupPrefix: OutboxHandler = async (raw, context) => { if (owner) throw new Error('Cannot clean storage for an existing Project') const prefix = prefixFor(payload.projectId) const uploads = payload.uploads ?? [] - for (const [index, upload] of uploads.entries()) { + for (let index = payload.uploadCursor ?? 0; index < uploads.length; index++) { + const upload = uploads[index] context.signal.throwIfAborted() try { await abortProviderUpload({ @@ -287,9 +306,9 @@ const cleanupPrefix: OutboxHandler = async (raw, context) => { throw error } if ((index + 1) % 20 === 0 || index === uploads.length - 1) - await context.checkpointPayload({ uploads: uploads.slice(index + 1) }) + await context.checkpointPayload({ uploadCursor: index + 1 }) if (context.deadlineAt && Date.now() + 5000 >= context.deadlineAt) { - await context.checkpointPayload({ uploads: uploads.slice(index + 1) }) + await context.checkpointPayload({ uploadCursor: index + 1 }) return continueOutboxHandler('Continuing retired Project upload cleanup') } } @@ -308,6 +327,17 @@ const cleanupPrefix: OutboxHandler = async (raw, context) => { deleted++ } if (keys.length >= PAGE_SIZE) return continueOutboxHandler('Continuing Project storage cleanup') + if (payload.retained) { + await context.checkpointPayload({ uploadCursor: 0 }) + return { + ...deferOutboxHandler( + 'Retired Project prefix remains subject to late transfer cleanup', + RETIRED_PREFIX_SWEEP_INTERVAL_MS, + false + ), + resetAttempts: true, + } + } } export const projectFilePrefixCleanupOutboxHandlers = { diff --git a/apps/sim/lib/projects/files/purge.ts b/apps/sim/lib/projects/files/purge.ts index 8a77ad8e4b5..8a172ee6abd 100644 --- a/apps/sim/lib/projects/files/purge.ts +++ b/apps/sim/lib/projects/files/purge.ts @@ -65,19 +65,18 @@ export async function purgeProjectFilesInTx( } if (purgedBytes !== expectedBillableBytes) throw new Error('Project storage changed during retirement') - const removedFolders = await tx - .delete(folder) - .where(and(eq(folder.projectId, projectId), eq(folder.resourceType, 'file'))) - .returning({ id: folder.id }) - if (removedFolders.length) - await tx.delete(publicShare).where( + const ownedFolders = and(eq(folder.projectId, projectId), eq(folder.resourceType, 'file')) + await tx + .delete(publicShare) + .where( and( eq(publicShare.resourceType, 'folder'), inArray( publicShare.resourceId, - removedFolders.map((row) => row.id) + tx.select({ id: folder.id }).from(folder).where(ownedFolders) ) ) ) + await tx.delete(folder).where(ownedFolders) return eventIds } diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts index 59821ccd553..bfe2a492e2f 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts @@ -517,7 +517,7 @@ function withVersionSnapshot( executor?: DbTransaction ): Promise { const readSnapshot = async (tx: DbTransaction) => { - const [row] = await tx + const query = tx .select({ key: workspaceFiles.key, sizeBytes: workspaceFiles.sizeBytes, @@ -531,6 +531,7 @@ function withVersionSnapshot( .from(workspaceFiles) .where(eq(workspaceFiles.id, file.id)) .limit(1) + const [row] = await (executor ? query.for('share') : query) const snapshot: WorkspaceFileVersionSubject = row ? { id: file.id, diff --git a/apps/sim/lib/uploads/documents/compile.test.ts b/apps/sim/lib/uploads/documents/compile.test.ts index 7e75fb084aa..f771a229aa2 100644 --- a/apps/sim/lib/uploads/documents/compile.test.ts +++ b/apps/sim/lib/uploads/documents/compile.test.ts @@ -35,6 +35,7 @@ vi.mock('@/lib/uploads/documents/compiled-store', () => ({ })) import { collectReferencedFileIds, compileDoc } from '@/lib/uploads/documents/compile' +import { DocCompileUserError } from '@/lib/uploads/documents/compile-error' const executeInSandboxMock = remoteSandboxMockFns.mockExecuteInSandbox @@ -53,52 +54,114 @@ describe('collectReferencedFileIds', () => { }) it('captures the id from getFileBase64(...) with single or double quotes', () => { - expect(collectReferencedFileIds(`await getFileBase64('${ID}')`)).toEqual(new Set([ID])) - expect(collectReferencedFileIds(`getFileBase64("abc_def-1")`)).toEqual(new Set(['abc_def-1'])) + expect(collectReferencedFileIds(`await getFileBase64('${ID}')`, 'javascript')).toEqual( + new Set([ID]) + ) + expect(collectReferencedFileIds(`getFileBase64("abc_def-1")`, 'javascript')).toEqual( + new Set(['abc_def-1']) + ) }) it('captures the id from pptx addImage(slide, id, opts) (second arg)', () => { const src = `await addImage(slide, '${ID}', { x: 1, y: 1, w: 2, h: 2 })` - expect(collectReferencedFileIds(src)).toEqual(new Set([ID])) + expect(collectReferencedFileIds(src, 'javascript')).toEqual(new Set([ID])) }) it('captures the id from docx addImage(id, opts) (first arg)', () => { const src = `const img = await addImage('docx-img-1', { width: 200, height: 100 })` - expect(collectReferencedFileIds(src)).toEqual(new Set(['docx-img-1'])) + expect(collectReferencedFileIds(src, 'javascript')).toEqual(new Set(['docx-img-1'])) }) it('captures the id from pdf drawImage(page, id, opts) (second arg)', () => { const src = `await drawImage(page, 'pdf-img-2', { x: 0, y: 0, width: 100, height: 100 })` - expect(collectReferencedFileIds(src)).toEqual(new Set(['pdf-img-2'])) + expect(collectReferencedFileIds(src, 'javascript')).toEqual(new Set(['pdf-img-2'])) }) it('still supports the legacy /home/user/inputs/ path form', () => { - expect(collectReferencedFileIds(`fs.readFileSync('/home/user/inputs/legacy-1')`)).toEqual( - new Set(['legacy-1']) + expect( + collectReferencedFileIds(`fs.readFileSync('/home/user/inputs/legacy-1')`, 'javascript') + ).toEqual(new Set(['legacy-1'])) + }) + + it('ignores helper examples and comments while resolving executable calls', () => { + const source = [ + '// getFileBase64("comment")', + '/* addImage(slide, "comment-block") */', + 'const example = \'getFileBase64("string-example")\'', + 'const template = `addImage("template-example")`', + 'await getFileBase64("actual")', + ].join('\n') + expect(collectReferencedFileIds(source, 'javascript')).toEqual(new Set(['actual'])) + }) + + it('ignores JavaScript regex literals and preserves calls after regex quotes', () => { + const source = [ + 'const example = /getFileBase64("regex-example")/', + "const quote = /'/; getFileBase64('actual')", + `const interpolation = \`\${/}/.test("}") ? getFileBase64("nested") : ""}\``, + 'const path = `/home/user/inputs/template-path`', + ].join('\n') + expect(collectReferencedFileIds(source, 'javascript')).toEqual( + new Set(['actual', 'nested', 'template-path']) + ) + }) + + it('distinguishes Python floor division and quoted examples from file reads', () => { + const source = [ + '# input_path("comment")', + "'''input_path(\"example\")'''", + 'ratio = 4 // 2; image = input_path("actual")', + ].join('\n') + expect(collectReferencedFileIds(source, 'python')).toEqual(new Set(['actual'])) + }) + + it('discovers Python f-string expressions without staging literal examples', () => { + const source = [ + `example = f"input_path('example') {{input_path('escaped-example')}}"`, + `path = f"{input_path('actual')}"`, + `raw = input_path(r'raw-id')`, + `nested = f"{f\"{input_path('nested')}\"}"`, + `formatted = f"{value:input_path('format-example'){input_path('width')}}"`, + `mapping = f"{ {'x': input_path('mapping')} }"`, + `static = input_path(f'static-id')`, + ].join('\n') + expect(collectReferencedFileIds(source, 'python')).toEqual( + new Set(['actual', 'raw-id', 'nested', 'width', 'mapping', 'static-id']) ) }) + it('reports malformed JavaScript as a document source error before compilation', async () => { + await expect( + compileDoc({ + source: `const image = getFileBase64('unterminated`, + fileName: 'report.pptx', + workspaceId: 'workspace-1', + filePrincipal: FILE_PRINCIPAL, + }) + ).rejects.toBeInstanceOf(DocCompileUserError) + }) + it('collects and dedupes ids across multiple call sites', () => { const src = ` await addImage(slide, 'logo-1', { x: 0, y: 0, w: 1, h: 1 }); const uri = await getFileBase64('logo-1'); await addImage(slide, 'crest-2', { x: 2, y: 0, w: 1, h: 1 }); ` - expect(collectReferencedFileIds(src)).toEqual(new Set(['logo-1', 'crest-2'])) + expect(collectReferencedFileIds(src, 'javascript')).toEqual(new Set(['logo-1', 'crest-2'])) }) it('does not match id-like strings outside the image helpers', () => { const src = `slide.addText('order ${ID} shipped', { x: 1, y: 1, w: 8, h: 1 })` - expect(collectReferencedFileIds(src)).toEqual(new Set()) + expect(collectReferencedFileIds(src, 'javascript')).toEqual(new Set()) }) it('does not match slide.addImage({ data }) — no fileId is present there', () => { const src = `slide.addImage({ data: base64Data, x: 1, y: 1, w: 2, h: 2 })` - expect(collectReferencedFileIds(src)).toEqual(new Set()) + expect(collectReferencedFileIds(src, 'javascript')).toEqual(new Set()) }) it('retains a full deck rebuild — every extracted image plus headroom fits the cap', () => { - const ids = collectReferencedFileIds(referencedFileSource(500)) + const ids = collectReferencedFileIds(referencedFileSource(500), 'javascript') expect(ids.size).toBe(500) expect(ids.has('file-0')).toBe(true) @@ -106,7 +169,7 @@ describe('collectReferencedFileIds', () => { }) it('stops collecting at the one-over-cap sentinel', () => { - const ids = collectReferencedFileIds(referencedFileSource(2000)) + const ids = collectReferencedFileIds(referencedFileSource(2000), 'javascript') expect(ids.size).toBe(501) }) diff --git a/apps/sim/lib/uploads/documents/compile.ts b/apps/sim/lib/uploads/documents/compile.ts index 80ea9bcde68..3fd5159feae 100644 --- a/apps/sim/lib/uploads/documents/compile.ts +++ b/apps/sim/lib/uploads/documents/compile.ts @@ -21,7 +21,10 @@ import { storeCompiledDoc, } from '@/lib/uploads/documents/compiled-store' import { PPTX_SHIM_JS } from '@/lib/uploads/documents/pptx-shim' -import { iterateDocumentFileReferences } from '@/lib/uploads/documents/references' +import { + type DocumentSourceLanguage, + iterateDocumentFileReferences, +} from '@/lib/uploads/documents/references' import { getFileExtension, getMimeTypeFromExtension } from '@/lib/uploads/utils/file-utils' import { readWorkspaceFileContent } from '@/lib/workspace-files/application/read-workspace-file-content' import { readWorkspaceFileMetadata } from '@/lib/workspace-files/application/read-workspace-file-metadata' @@ -112,6 +115,20 @@ function isSimdocDeckSource(source: string): boolean { return source.trimStart().startsWith(SIMDOC_DECK_MARKER) } +/** Selects the source dialect for dependency discovery, including retained legacy documents. */ +export function getDocumentSourceLanguage( + source: string, + format: E2BDocFormat, + sourceMime?: string +): DocumentSourceLanguage { + if ((format.ext === 'pptx' || format.ext === 'docx') && isSimdocDeckSource(source)) + return 'python' + if (sourceMime === 'text/x-pdflibjs') return 'javascript' + if (sourceMime === PYTHON_PDF_SOURCE_MIME || sourceMime === PYTHON_XLSX_SOURCE_MIME) + return 'python' + return format.engine === 'node' ? 'javascript' : 'python' +} + // The doc source is user/LLM-controlled, so bound how much it can pull into the // sandbox by BYTES (per file and total) — an authenticated member must not be // able to force very large workspace downloads to be base64-held in-process per @@ -161,8 +178,12 @@ interface CompiledDocResult { * isolated-VM broker reaches files the static scan cannot see. Either one means these bytes can * change while this file's own stored source does not. */ -function touchesReferencedFiles(source: string, accessedCount: number): boolean { - return accessedCount > 0 || collectReferencedFileIds(source).size > 0 +function touchesReferencedFiles( + source: string, + accessedCount: number, + language: DocumentSourceLanguage +): boolean { + return accessedCount > 0 || collectReferencedFileIds(source, language).size > 0 } function referencedImageIdentities( @@ -176,17 +197,13 @@ function referencedImageIdentities( })) } -/** - * Collects the workspace file ids a doc source references — from the injected - * image-helper call sites and the legacy `/home/user/inputs/` path. Matching - * is scoped to the helper calls (not bare id-like strings in slide text), and the - * caller skips any id that does not resolve to a real file, so over-matching is - * harmless. Retention stops at one over the reference cap: callers need only - * distinguish no references, an admissible set, and an oversized set. - */ -export function collectReferencedFileIds(source: string): Set { +/** Collects static input identities, retaining one over the cap so callers can reject oversized sets. */ +export function collectReferencedFileIds( + source: string, + language: DocumentSourceLanguage +): Set { const ids = new Set() - for (const { fileId } of iterateDocumentFileReferences(source)) { + for (const { fileId } of iterateDocumentFileReferences(source, language)) { ids.add(fileId) if (ids.size > MAX_REFERENCED_INPUTS) return ids } @@ -194,10 +211,9 @@ export function collectReferencedFileIds(source: string): Set { } async function resolveReferencedImages( - source: string, workspaceId: string, principal: Principal, - ids = collectReferencedFileIds(source) + ids: Set ): Promise { if (ids.size > MAX_REFERENCED_INPUTS) { // User-fixable, not transient: each reference costs a metadata read, so an @@ -561,7 +577,11 @@ async function buildCompiledDoc( referencedImages.artifactIdentity ), contentType: fmt.contentType, - dependsOnReferencedFiles: touchesReferencedFiles(args.source, contributingFiles.length), + dependsOnReferencedFiles: touchesReferencedFiles( + args.source, + contributingFiles.length, + getDocumentSourceLanguage(args.source, fmt) + ), ...(contributingFiles.length > 0 ? { contributingFiles } : {}), } } @@ -668,7 +688,8 @@ async function compileDocInLegacySandbox( contentType: fmt.contentType, dependsOnReferencedFiles: touchesReferencedFiles( args.source, - cached.contributingFiles?.length ?? 0 + cached.contributingFiles?.length ?? 0, + 'javascript' ), ...(cached.contributingFiles && cached.contributingFiles.length > 0 ? { contributingFiles: cached.contributingFiles } @@ -692,7 +713,11 @@ async function compileDocInLegacySandbox( return { buffer, contentType: fmt.contentType, - dependsOnReferencedFiles: touchesReferencedFiles(args.source, contributingFiles.size), + dependsOnReferencedFiles: touchesReferencedFiles( + args.source, + contributingFiles.size, + 'javascript' + ), ...(contributingFiles.size > 0 ? { contributingFiles: [...contributingFiles.values()] } : {}), } } @@ -709,9 +734,8 @@ export async function compileDoc(args: CompileArgs): Promise if (!fmt) throw new Error(`Unsupported document format: ${fileName}`) if (!isDocSandboxEnabled) return compileDocInLegacySandbox(args, fmt) - const referencedFileIds = collectReferencedFileIds(source) + const referencedFileIds = collectReferencedFileIds(source, getDocumentSourceLanguage(source, fmt)) const referencedImages = await resolveReferencedImages( - source, workspaceId, args.filePrincipal, referencedFileIds @@ -742,7 +766,11 @@ export async function compileDoc(args: CompileArgs): Promise referencedImages.artifactIdentity ), contentType: fmt.contentType, - dependsOnReferencedFiles: touchesReferencedFiles(source, contributingFiles.length), + dependsOnReferencedFiles: touchesReferencedFiles( + source, + contributingFiles.length, + getDocumentSourceLanguage(source, fmt) + ), ...(contributingFiles.length > 0 ? { contributingFiles } : {}), } } @@ -772,7 +800,7 @@ async function loadCompiledDocByExt( const fmt = await getE2BDocFormat(`x.${ext}`) if (!fmt) return null const readOptions: CompiledDocReadOptions = { maxBytes: options.maxBytes, signal: options.signal } - const referencedFileIds = collectReferencedFileIds(source) + const referencedFileIds = collectReferencedFileIds(source, getDocumentSourceLanguage(source, fmt)) if (!options.filePrincipal) { if (referencedFileIds.size === 0) { const buffer = await loadCompiledDoc(workspaceId, source, fmt.ext, undefined, readOptions) @@ -798,7 +826,6 @@ async function loadCompiledDocByExt( return legacyBuffer ? { buffer: legacyBuffer, contentType: fmt.contentType } : null } const referencedImages = await resolveReferencedImages( - source, workspaceId, options.filePrincipal, referencedFileIds @@ -942,7 +969,10 @@ export async function resolveServableDocBytes(args: { if (isCompiledDocumentBuffer(fileName, rawBuffer)) { return { buffer: rawBuffer, - contentType: getMimeTypeFromExtension(getFileExtension(fileName)), + contentType: + getFileExtension(fileName) === 'webm' + ? 'video/webm' + : getMimeTypeFromExtension(getFileExtension(fileName)), dependsOnReferencedFiles: false, } } @@ -950,20 +980,26 @@ export async function resolveServableDocBytes(args: { if (!format && extNoDot !== 'xlsx') { return { buffer: rawBuffer, - contentType: getMimeTypeFromExtension(getFileExtension(fileName)), + contentType: + getFileExtension(fileName) === 'webm' + ? 'video/webm' + : getMimeTypeFromExtension(getFileExtension(fileName)), dependsOnReferencedFiles: false, } } const source = rawBuffer.toString('utf-8') + const fmt = await getE2BDocFormat(fileName) + if (!fmt) throw new Error(`Unsupported document format: ${fileName}`) if (workspaceId) { if (!isDocSandboxEnabled) { - const fmt = await getE2BDocFormat(fileName) - if (!fmt) throw new Error(`Unsupported document format: ${fileName}`) return compileDocInLegacySandbox({ source, fileName, workspaceId, ownerKey, signal }, fmt) } - const referencedFileIds = collectReferencedFileIds(source) + const referencedFileIds = collectReferencedFileIds( + source, + getDocumentSourceLanguage(source, fmt) + ) if (referencedFileIds.size > 0) { if (!filePrincipal) { const published = await loadCompiledDocByExt(workspaceId, source, extNoDot, { @@ -1000,7 +1036,8 @@ export async function resolveServableDocBytes(args: { // entry can hold contributor identities even though this call passed none. dependsOnReferencedFiles: touchesReferencedFiles( source, - cached.contributingFiles?.length ?? 0 + cached.contributingFiles?.length ?? 0, + 'javascript' ), ...(cached.contributingFiles && cached.contributingFiles.length > 0 ? { contributingFiles: cached.contributingFiles } @@ -1017,6 +1054,6 @@ export async function resolveServableDocBytes(args: { return { buffer: compiled, contentType: format.contentType, - dependsOnReferencedFiles: touchesReferencedFiles(source, 0), + dependsOnReferencedFiles: touchesReferencedFiles(source, 0, 'javascript'), } } diff --git a/apps/sim/lib/uploads/documents/index.ts b/apps/sim/lib/uploads/documents/index.ts index c4f537779fe..5641079904e 100644 --- a/apps/sim/lib/uploads/documents/index.ts +++ b/apps/sim/lib/uploads/documents/index.ts @@ -1,5 +1,6 @@ export { collectReferencedFileIds, + getDocumentSourceLanguage, getE2BDocFormat, isCompiledDocumentBuffer, } from './compile' diff --git a/apps/sim/lib/uploads/documents/references.ts b/apps/sim/lib/uploads/documents/references.ts index dc0ede9044e..fa3568646a1 100644 --- a/apps/sim/lib/uploads/documents/references.ts +++ b/apps/sim/lib/uploads/documents/references.ts @@ -1,21 +1,171 @@ +import { tokenizer } from 'acorn' +import { DocCompileUserError } from '@/lib/uploads/documents/compile-error' + +export type DocumentSourceLanguage = 'javascript' | 'python' + interface DocumentFileReference { fileId: string start: number end: number } -const INPUT_PATH_RE = /\/home\/user\/inputs\/([A-Za-z0-9_-]+)/g -const FILE_HELPER_RE = - /\b(?:getFileBase64|addImage|drawImage|input_path)\(\s*(?:[A-Za-z_$][\w$]*\s*,\s*)?['"]([A-Za-z0-9_-]+)['"]/g - -/** Shares the compiler's static image/template grammar with source copies; bare text is never a file reference. */ -export function* iterateDocumentFileReferences(source: string): Generator { - for (const pattern of [INPUT_PATH_RE, FILE_HELPER_RE]) { - for (const match of source.matchAll(pattern)) { - const fileId = match[1] - if (!fileId) continue - const start = match.index + match[0].lastIndexOf(fileId) - yield { fileId, start, end: start + fileId.length } +interface SourceToken { + kind: 'word' | 'string' | 'punctuation' + value: string + start: number +} + +const FILE_HELPERS = new Set(['getFileBase64', 'addImage', 'drawImage', 'input_path']) + +function* javascriptTokens(source: string): Generator { + try { + const tokens = tokenizer(source, { ecmaVersion: 'latest', allowHashBang: true }) + for (let token = tokens.getToken(); token.type.label !== 'eof'; token = tokens.getToken()) { + const label = token.type.label + if (label === 'string') { + yield { + kind: 'string', + value: source.slice(token.start + 1, token.end - 1), + start: token.start + 1, + } + } else if (label === 'template') { + if (source[token.start - 1] === '`' && source[token.end] === '`') { + yield { kind: 'string', value: source.slice(token.start, token.end), start: token.start } + } + } else { + yield { + kind: label === 'name' ? 'word' : 'punctuation', + value: source.slice(token.start, token.end), + start: token.start, + } + } + } + } catch (error) { + if (error instanceof SyntaxError) throw new DocCompileUserError(error.message) + throw error + } +} + +function* pythonTokens(source: string): Generator { + let cursor = 0 + function* stringTokens(quote: string, formatted: boolean, depth: number): Generator { + if (depth > 100) throw new DocCompileUserError('Document source strings are nested too deeply') + cursor += quote.length + const contentStart = cursor + let interpolated = false + while (cursor < source.length && !source.startsWith(quote, cursor)) { + const start = cursor + const char = source[cursor++] + if (formatted && (char === '{' || char === '}') && source[cursor] === char) { + cursor++ + } else if (formatted && char === '{') { + interpolated = true + yield { kind: 'punctuation', value: '{', start } + yield* codeTokens(true, depth + 1) + } else if (char === '\\' && !(formatted && /[{}]/.test(source[cursor] ?? ''))) { + cursor++ + } + } + if (cursor >= source.length) throw new DocCompileUserError('Unterminated Python string') + if (!interpolated) { + yield { kind: 'string', value: source.slice(contentStart, cursor), start: contentStart } + } + cursor += quote.length + if (interpolated) yield { kind: 'punctuation', value: quote, start: cursor - quote.length } + } + + function* formatTokens(depth: number): Generator { + while (cursor < source.length) { + const start = cursor + const char = source[cursor++] + if (char === '}') { + yield { kind: 'punctuation', value: char, start } + return + } + if (char === '{') { + yield { kind: 'punctuation', value: char, start } + yield* codeTokens(true, depth + 1) + } + } + throw new DocCompileUserError('Unterminated Python format specification') + } + + function* codeTokens(replacement: boolean, depth: number): Generator { + if (depth > 100) + throw new DocCompileUserError('Document source expressions are nested too deeply') + let brackets = 0 + while (cursor < source.length) { + const start = cursor + const char = source[cursor++] + if (/\s/.test(char)) continue + if (char === '#') { + while (cursor < source.length && source[cursor] !== '\n') cursor++ + continue + } + if (char === '"' || char === "'") { + const quote = source.slice(start, start + 3) === char.repeat(3) ? char.repeat(3) : char + cursor = start + yield* stringTokens(quote, false, depth) + continue + } + if (/[A-Za-z_]/.test(char)) { + while (cursor < source.length && /\w/.test(source[cursor])) cursor++ + const word = source.slice(start, cursor) + const next = source[cursor] + if (/^(?:r|u|b|f|br|rb|fr|rf)$/i.test(word) && (next === '"' || next === "'")) { + const quote = source.slice(cursor, cursor + 3) === next.repeat(3) ? next.repeat(3) : next + yield* stringTokens(quote, /f/i.test(word), depth) + } else { + yield { kind: 'word', value: word, start } + } + continue + } + yield { kind: 'punctuation', value: char, start } + if (replacement && brackets === 0) { + if (char === '}') return + if (char === ':') { + yield* formatTokens(depth) + return + } + } + if ('([{'.includes(char)) brackets++ + else if (')]}'.includes(char)) brackets-- + } + if (replacement) throw new DocCompileUserError('Unterminated Python replacement expression') + } + yield* codeTokens(false, 0) +} + +/** Shares static helper and input-path references between document compilation and copying. */ +export function* iterateDocumentFileReferences( + source: string, + language: DocumentSourceLanguage +): Generator { + const previous: SourceToken[] = [] + for (const token of language === 'javascript' ? javascriptTokens(source) : pythonTokens(source)) { + if (token.kind === 'string') { + const path = /^\/home\/user\/inputs\/([A-Za-z0-9_-]+)$/.exec(token.value) + const helper = + previous.at(-1)?.value === '(' + ? previous.at(-2) + : previous.at(-1)?.value === ',' && + previous.at(-2)?.kind === 'word' && + previous.at(-3)?.value === '(' + ? previous.at(-4) + : undefined + const fileId = + path?.[1] ?? + (helper?.kind === 'word' && + FILE_HELPERS.has(helper.value) && + /^[A-Za-z0-9_-]+$/.test(token.value) + ? token.value + : undefined) + if (fileId) { + const start = token.start + (path ? '/home/user/inputs/'.length : 0) + yield { fileId, start, end: start + fileId.length } + } } + previous.push(token) + if (previous.length > 4) previous.shift() } } diff --git a/apps/sim/lib/uploads/documents/servable.test.ts b/apps/sim/lib/uploads/documents/servable.test.ts index 5f362fd36c6..a215fbbc28d 100644 --- a/apps/sim/lib/uploads/documents/servable.test.ts +++ b/apps/sim/lib/uploads/documents/servable.test.ts @@ -53,6 +53,7 @@ const { mockExecuteInSandbox } = remoteSandboxMockFns const WORKSPACE_ID = '550e8400-e29b-41d4-a716-446655440000' const FILE_PRINCIPAL = { kind: 'session', userId: 'user-1' } as const const PDF_MAGIC = Buffer.from('%PDF-1.7\n...binary...') +const LEGACY_PDF_SOURCE = Buffer.from('pdf.addPage();', 'utf-8') const PDF_SOURCE = Buffer.from('from reportlab.pdfgen import canvas\n# generates a PDF', 'utf-8') const XLSX_SOURCE = Buffer.from('from openpyxl import Workbook\n# generates an xlsx', 'utf-8') @@ -343,7 +344,7 @@ describe('resolveServableDocBytes', () => { mockRunSandboxTask.mockResolvedValue(compiled) const result = await resolveServableDocBytes({ - rawBuffer: PDF_SOURCE, + rawBuffer: LEGACY_PDF_SOURCE, fileName: 'report.pdf', workspaceId: WORKSPACE_ID, }) @@ -352,7 +353,7 @@ describe('resolveServableDocBytes', () => { expect(result.contentType).toBe('application/pdf') expect(mockRunSandboxTask).toHaveBeenCalledWith( 'pdf-generate', - { code: PDF_SOURCE.toString('utf-8'), workspaceId: WORKSPACE_ID }, + { code: LEGACY_PDF_SOURCE.toString('utf-8'), workspaceId: WORKSPACE_ID }, expect.objectContaining({}) ) }) @@ -365,7 +366,7 @@ describe('resolveServableDocBytes', () => { await expect( resolveServableDocBytes({ - rawBuffer: PDF_SOURCE, + rawBuffer: LEGACY_PDF_SOURCE, fileName: 'report.pdf', workspaceId: WORKSPACE_ID, }) diff --git a/apps/sim/lib/workspace-files/search/dispatcher.ts b/apps/sim/lib/workspace-files/search/dispatcher.ts index 9426b02d27e..2a4f8161027 100644 --- a/apps/sim/lib/workspace-files/search/dispatcher.ts +++ b/apps/sim/lib/workspace-files/search/dispatcher.ts @@ -470,7 +470,15 @@ async function claimQueuedOwnerJobs( ), and( eq(workspaceFileSearchRevision.status, 'ready'), - sql`EXISTS (SELECT 1 FROM file_search_dependency dependency LEFT JOIN workspace_files input ON input.id = dependency.file_id WHERE dependency.build_id = ${workspaceFileSearchRevision.buildId} AND (input.id IS NULL OR input.deleted_at IS NOT NULL OR input.key <> dependency.key OR input.content_updated_at <> dependency.source_content_updated_at))` + or( + ...owners.map((owner) => + and( + sql`coalesce(${workspaceFileSearchRevision.entityType}, 'workspace') = ${owner.entityType}`, + sql`coalesce(${workspaceFileSearchRevision.entityId}, ${workspaceFileSearchRevision.workspaceId}) = ${owner.entityId}`, + sql`NOT (${currentFileSearchDependencies(workspaceFileSearchRevision.buildId, owner)})` + ) + ) + ) ) ) ) diff --git a/apps/sim/lib/workspace-files/search/owners.integration.ts b/apps/sim/lib/workspace-files/search/owners.integration.ts index 52d9fd689b2..98502ae0f90 100644 --- a/apps/sim/lib/workspace-files/search/owners.integration.ts +++ b/apps/sim/lib/workspace-files/search/owners.integration.ts @@ -4,6 +4,7 @@ import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { db, runOutsideTransactionContext } from '@sim/db' import { + fileSearchDispatchQueue, folder, member, organization, @@ -19,12 +20,13 @@ import { } from '@sim/db/schema' import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createSessionPrincipal } from '@sim/testing/factories/principal.factory' +import { createDeferred } from '@sim/testing/helpers/deferred' import { setUploadDirServer, uploadsSetupMock } from '@sim/testing/mocks/uploads-setup.mock' import { getErrorMessage } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { Document, Packer, Paragraph } from 'docx' -import { eq, inArray, sql } from 'drizzle-orm' +import { and, eq, inArray, or, sql } from 'drizzle-orm' import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('@/lib/uploads/core/setup.server', () => uploadsSetupMock) @@ -37,6 +39,7 @@ import { storeCompiledDoc } from '@/lib/uploads/documents/compiled-store' import { fileDocumentInputIdentity } from '@/lib/uploads/documents/input-identity' import { searchWorkspaceFileContent } from '@/lib/workspace-files/application/search-workspace-file-content' import { resolveFileSearchFolderScope } from '@/lib/workspace-files/search/delivery' +import { prepareWorkspaceFileSearchDispatch } from '@/lib/workspace-files/search/dispatcher' import { appendFileSearchChunks, beginFileSearchBuild, @@ -288,6 +291,121 @@ describe('owner-scoped indexed file search on PostgreSQL and stored bytes', () = } ) + check('requeues the former owner and rebuilds derived files after an input moves', async () => { + const f = await fixture() + const destination = await fixture() + const dependencyId = generateId() + const sourceId = generateId() + const [dependency] = await db + .insert(workspaceFiles) + .values({ + id: dependencyId, + userId: f.ownerId, + workspaceId: f.workspaceId, + context: 'workspace', + key: `workspace/${f.workspaceId}/${dependencyId}/input.txt`, + originalName: 'input.txt', + contentType: 'text/plain', + sizeBytes: 5, + }) + .returning() + await db.insert(workspaceFiles).values({ + id: sourceId, + userId: f.ownerId, + workspaceId: f.workspaceId, + context: 'workspace', + key: `workspace/${f.workspaceId}/${sourceId}/derived.docx`, + originalName: 'derived.docx', + contentType: 'text/x-docxjs', + sizeBytes: 10, + }) + const claimed = await claim(sourceId) + const build = await beginFileSearchBuild( + { + workspaceId: f.workspaceId, + fileId: sourceId, + sourceContentUpdatedAt: new Date(claimed.sourceContentUpdatedAt), + }, + claimed.dispatchToken + ) + if (!build) throw new Error('Derived file build was not admitted') + expect( + await publishFileSearchBuild( + build, + { + status: 'ready', + lineCount: 0, + indexedBytes: 0, + chunkCount: 0, + dependencies: [ + { + fileId: dependencyId, + key: dependency.key, + sourceContentUpdatedAt: dependency.contentUpdatedAt, + }, + ], + }, + signal + ) + ).toBe(true) + await prepareWorkspaceFileSearchDispatch(0) + await db + .delete(fileSearchDispatchQueue) + .where( + and( + eq(fileSearchDispatchQueue.entityType, 'workspace'), + eq(fileSearchDispatchQueue.entityId, f.workspaceId) + ) + ) + await db + .update(workspaceFiles) + .set({ workspaceId: destination.workspaceId }) + .where(eq(workspaceFiles.id, dependencyId)) + const queued = await db + .select() + .from(fileSearchDispatchQueue) + .where( + and( + eq(fileSearchDispatchQueue.entityType, 'workspace'), + eq(fileSearchDispatchQueue.entityId, f.workspaceId) + ) + ) + expect(queued).toHaveLength(1) + const locked = createDeferred() + const release = createDeferred() + const blocker = runOutsideTransactionContext(() => + db.transaction(async (tx) => { + await tx.select().from(workspaceFiles).where(eq(workspaceFiles.id, sourceId)).for('update') + locked.resolve() + await release.promise + }) + ) + await locked.promise + try { + await prepareWorkspaceFileSearchDispatch() + const waiting = await db + .select() + .from(fileSearchDispatchQueue) + .where( + and( + eq(fileSearchDispatchQueue.entityType, 'workspace'), + eq(fileSearchDispatchQueue.entityId, f.workspaceId) + ) + ) + expect(waiting).toHaveLength(1) + } finally { + release.resolve() + await blocker + } + const dispatch = await prepareWorkspaceFileSearchDispatch() + expect(dispatch.payloads.some((payload) => payload.fileId === sourceId)).toBe(true) + const [revision] = await db + .select() + .from(workspaceFileSearchRevision) + .where(eq(workspaceFileSearchRevision.fileId, sourceId)) + expect(revision).toMatchObject({ status: 'pending', buildId: null }) + }) + check('queues canonical Project revisions and isolates workspace and Project text', async () => { const f = await fixture() const projectFile = await create(f, 'projectneedle owner-isolated') @@ -783,9 +901,15 @@ afterAll(async () => { .select({ id: workspaceFiles.id }) .from(workspaceFiles) .where( - inArray( - workspaceFiles.projectId, - fixtures.flatMap((f) => [f.projectId, f.workspaceId]) + or( + inArray( + workspaceFiles.projectId, + fixtures.map((f) => f.projectId) + ), + inArray( + workspaceFiles.workspaceId, + fixtures.map((f) => f.workspaceId) + ) ) ) ).map((row) => row.id) @@ -811,6 +935,12 @@ afterAll(async () => { } } for (const f of fixtures) { + await db.execute( + sql`DELETE FROM file_search_dispatch_queue WHERE (entity_type = 'project' AND entity_id = ${f.projectId}) OR (entity_type = 'workspace' AND entity_id = ${f.workspaceId})` + ) + await db.execute( + sql`DELETE FROM workspace_file_search_dispatch_queue WHERE workspace_id = ${f.workspaceId}` + ) await db.delete(folder).where(eq(folder.projectId, f.projectId)) await deleteWorkspaceFixture(db, eq(workspace.id, f.workspaceId)) await db.delete(project).where(eq(project.id, f.projectId)) diff --git a/apps/sim/lib/workspace-files/search/source.ts b/apps/sim/lib/workspace-files/search/source.ts index dfda0873bf3..15580a402a8 100644 --- a/apps/sim/lib/workspace-files/search/source.ts +++ b/apps/sim/lib/workspace-files/search/source.ts @@ -6,9 +6,11 @@ import { assertKnownSizeWithinLimit } from '@/lib/core/utils/stream-limits' import { downloadFile } from '@/lib/uploads/core/storage-service' import { collectReferencedFileIds, + getDocumentSourceLanguage, getE2BDocFormat, isCompiledDocumentBuffer, } from '@/lib/uploads/documents' +import { DocCompileUserError } from '@/lib/uploads/documents/compile-error' import { compiledArtifactKey, loadCompiledDoc } from '@/lib/uploads/documents/compiled-store' import { fileDocumentInputIdentity } from '@/lib/uploads/documents/input-identity' import { @@ -63,7 +65,19 @@ export async function loadFileSearchSource( if (!format || isCompiledDocumentBuffer(file.originalName, raw)) return { file, bytes: { buffer: raw, kind: 'stored' }, dependencies: [] } const source = raw.toString('utf8') - const ids = [...new Set(collectReferencedFileIds(source))] + let ids: string[] + try { + ids = [ + ...collectReferencedFileIds( + source, + getDocumentSourceLanguage(source, format, file.contentType) + ), + ] + } catch (error) { + if (error instanceof DocCompileUserError) + throw new FileSearchExclusionError('incomplete_extraction') + throw error + } if (ids.length > FILE_SEARCH_MAX_DEPENDENCIES) throw new FileSearchExclusionError('incomplete_extraction') const inputs = diff --git a/apps/sim/package.json b/apps/sim/package.json index cbb1ef66c78..df36dd59d52 100644 --- a/apps/sim/package.json +++ b/apps/sim/package.json @@ -164,6 +164,7 @@ "@xterm/addon-webgl": "0.19.0", "@xterm/xterm": "6.0.0", "@xyflow/react": "12.11.3", + "acorn": "8.17.0", "ajv": "8.18.0", "archiver": "8.0.0", "better-auth": "1.6.27", diff --git a/bun.lock b/bun.lock index 26b986ecb4b..b6440e85648 100644 --- a/bun.lock +++ b/bun.lock @@ -290,6 +290,7 @@ "@xterm/addon-webgl": "0.19.0", "@xterm/xterm": "6.0.0", "@xyflow/react": "12.11.3", + "acorn": "8.17.0", "ajv": "8.18.0", "archiver": "8.0.0", "better-auth": "1.6.27", diff --git a/packages/auth/src/principal.ts b/packages/auth/src/principal.ts index aea30a99b8c..c5341644749 100644 --- a/packages/auth/src/principal.ts +++ b/packages/auth/src/principal.ts @@ -184,9 +184,9 @@ export type BoundWorkflowExecutionDelegatedPrincipal = WorkflowExecutionDelegate export type DelegatedPrincipal = SubjectDelegatedPrincipal | WorkflowExecutionDelegatedPrincipal -/** Search-only authority delegated by a current organization member. */ export type ResourceEntityType = 'workspace' | 'project' | 'organization' | 'user' +/** Search-only authority delegated by a current organization member. */ interface OrganizationDelegatedPrincipalBase { kind: 'organization_delegated' organizationId: string diff --git a/packages/db/migrations/0403_file_search_owner_scope.sql b/packages/db/migrations/0403_file_search_owner_scope.sql index 7dab1fc2309..95ae86cc6e9 100644 --- a/packages/db/migrations/0403_file_search_owner_scope.sql +++ b/packages/db/migrations/0403_file_search_owner_scope.sql @@ -132,6 +132,15 @@ BEGIN UPDATE workspace_file_search_build SET expires_at = now() WHERE id = (SELECT build_id FROM workspace_file_search_revision WHERE file_id = NEW.id); DELETE FROM workspace_file_search_revision WHERE file_id = NEW.id; + IF ROW(NEW.context, NEW.workspace_id, NEW.project_id) + IS DISTINCT FROM ROW(OLD.context, OLD.workspace_id, OLD.project_id) THEN + owner_type := CASE WHEN OLD.project_id IS NOT NULL THEN 'project' WHEN OLD.context = 'workspace' THEN 'workspace' END; + owner_id := coalesce(OLD.project_id, OLD.workspace_id); + IF owner_type IN ('workspace', 'project') AND OLD.context = owner_type AND owner_id IS NOT NULL THEN + INSERT INTO file_search_dispatch_queue (entity_type, entity_id) VALUES (owner_type, owner_id) + ON CONFLICT (entity_type, entity_id) DO UPDATE SET updated_at = now(); + END IF; + END IF; END IF; owner_type := CASE WHEN NEW.project_id IS NOT NULL THEN 'project' WHEN NEW.context = 'workspace' THEN 'workspace' END; owner_id := coalesce(NEW.project_id, NEW.workspace_id); diff --git a/packages/db/scripts/push.ts b/packages/db/scripts/push.ts index 5c2725097f2..eafdf43c0b7 100644 --- a/packages/db/scripts/push.ts +++ b/packages/db/scripts/push.ts @@ -5,6 +5,7 @@ import postgres, { type Sql } from 'postgres' const logger = createLogger('DatabasePush') const RECONCILIATION_COMMANDS = [ + ['bun', '--env-file=.env', 'run', './scripts/reconcile-file-ownership.ts'], ['bun', '--env-file=.env', 'run', './scripts/reconcile-credential-group-resource-policies.ts'], ['bun', '--env-file=.env', 'run', './scripts/reconcile-oauth-provider.ts'], ['bun', '--env-file=.env', 'run', './script-migrations/0016_backfill_search_vectors.ts'], diff --git a/packages/db/scripts/reconcile-file-ownership.ts b/packages/db/scripts/reconcile-file-ownership.ts new file mode 100644 index 00000000000..ddfef0dc1cc --- /dev/null +++ b/packages/db/scripts/reconcile-file-ownership.ts @@ -0,0 +1,41 @@ +import { readFile } from 'node:fs/promises' +import { createLogger } from '@sim/logger' +import postgres from 'postgres' + +const logger = createLogger('FileOwnershipReconciliation') +const url = process.env.DATABASE_URL +if (!url) throw new Error('Missing DATABASE_URL') +const sql = postgres(url, { max: 1, connect_timeout: 10 }) + +const migrations = [ + '0313_puzzling_zodiak.sql', + '0359_workspace_file_search_chunks.sql', + '0398_file_entity_ownership.sql', + '0399_file_folder_version_ownership.sql', + '0400_file_creator_lifetime.sql', + '0402_public_share_entity_ownership.sql', + '0403_file_search_owner_scope.sql', +] + +try { + await sql.begin(async (tx) => { + for (const migration of migrations) { + const source = await readFile(new URL(`../migrations/${migration}`, import.meta.url), 'utf8') + for (const chunk of source.split('--> statement-breakpoint')) { + const statement = chunk.replace(/^(?:\s|--[^\n]*(?:\n|$))*/, '') + if ( + !/^(?:CREATE (?:OR REPLACE FUNCTION|(?:CONSTRAINT )?TRIGGER)|DROP TRIGGER)\b/i.test( + statement + ) + ) + continue + const trigger = /^CREATE (?:CONSTRAINT )?TRIGGER (\w+)[\s\S]*?\bON (\w+)/i.exec(statement) + if (trigger) await tx.unsafe(`DROP TRIGGER IF EXISTS "${trigger[1]}" ON "${trigger[2]}"`) + await tx.unsafe(statement) + } + } + }) + logger.info('File ownership functions and triggers reconciled') +} finally { + await sql.end() +} From a8cee8a1a420fade25bb9faaa8cb1d588edfa522 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 6 Oct 2026 11:34:08 -0700 Subject: [PATCH 028/113] test(files): complete Project detachment database fixture --- apps/sim/lib/workspaces/organization-workspaces.integration.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/sim/lib/workspaces/organization-workspaces.integration.ts b/apps/sim/lib/workspaces/organization-workspaces.integration.ts index 331b66d33b8..e6ca640911a 100644 --- a/apps/sim/lib/workspaces/organization-workspaces.integration.ts +++ b/apps/sim/lib/workspaces/organization-workspaces.integration.ts @@ -26,6 +26,7 @@ const database = drizzle(connection, { schema }) beforeAll(async () => { await connection.unsafe(`CREATE SCHEMA "${schemaName}"`) await connection.unsafe(` + CREATE TABLE project (id text PRIMARY KEY, organization_id text, owner_id text); CREATE TABLE project_workspace (project_id text NOT NULL, workspace_id text UNIQUE NOT NULL); CREATE TABLE member (id text PRIMARY KEY, organization_id text, user_id text, role text); CREATE TABLE organization (id text PRIMARY KEY, storage_used_bytes bigint NOT NULL); From 8c38f1863fa327d477ac7d4760f5320f6a5e206a Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 6 Oct 2026 11:38:53 -0700 Subject: [PATCH 029/113] test(files): seed required Project memberships in integration fixtures --- .../desktop-tool-authorization.integration.ts | 5 +++-- apps/sim/lib/table/row-changes.integration.ts | 14 ++++++++++---- .../create-workflow-with-state.integration.ts | 5 +++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/sim/lib/mothership/tools/client/desktop-tool-authorization.integration.ts b/apps/sim/lib/mothership/tools/client/desktop-tool-authorization.integration.ts index ee9d4f1fc96..09dc4526a57 100644 --- a/apps/sim/lib/mothership/tools/client/desktop-tool-authorization.integration.ts +++ b/apps/sim/lib/mothership/tools/client/desktop-tool-authorization.integration.ts @@ -25,6 +25,7 @@ vi.mock('@/lib/auth', () => authMock) import { db } from '@sim/db' import { copilotAsyncToolCalls, copilotChats, copilotRuns, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { NextRequest } from 'next/server' @@ -203,7 +204,7 @@ describe.runIf(Boolean(redisUrl))('desktop tool calls the server no longer admit createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Desktop authorization fixture', ownerId: userId, @@ -224,7 +225,7 @@ describe.runIf(Boolean(redisUrl))('desktop tool calls the server no longer admit afterAll(async () => { await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/lib/table/row-changes.integration.ts b/apps/sim/lib/table/row-changes.integration.ts index 4042b5ae006..7c27baa75ef 100644 --- a/apps/sim/lib/table/row-changes.integration.ts +++ b/apps/sim/lib/table/row-changes.integration.ts @@ -3,9 +3,11 @@ * moves it into the definition row without changing what a reader sees, under concurrent appends. */ import { db } from '@sim/db' -import { userTableDefinitions } from '@sim/db/schema' +import { userTableDefinitions, workspace } from '@sim/db/schema' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' +import { eq } from 'drizzle-orm' import postgres from 'postgres' import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { @@ -63,12 +65,16 @@ describe('table row-change log against real PostgreSQL', () => { beforeAll(async () => { await control`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at) VALUES (${userId}, 'Row change fixture', ${`${userId}@example.test`}, true, now(), now())` - await control`INSERT INTO workspace (id, name, owner_id, billed_account_user_id) - VALUES (${workspaceId}, 'Row change fixtures', ${userId}, ${userId})` + await insertWorkspaceFixture(db, { + id: workspaceId, + name: 'Row change fixtures', + ownerId: userId, + billedAccountUserId: userId, + }) }) afterAll(async () => { - await control`DELETE FROM workspace WHERE id = ${workspaceId}` + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await control`DELETE FROM "user" WHERE id = ${userId}` await control.end() }) diff --git a/apps/sim/lib/workflows/orchestration/create-workflow-with-state.integration.ts b/apps/sim/lib/workflows/orchestration/create-workflow-with-state.integration.ts index 9b4f9f10674..864925778ef 100644 --- a/apps/sim/lib/workflows/orchestration/create-workflow-with-state.integration.ts +++ b/apps/sim/lib/workflows/orchestration/create-workflow-with-state.integration.ts @@ -3,6 +3,7 @@ import { mkdir, writeFile } from 'node:fs/promises' import { dirname, resolve } from 'node:path' import { db } from '@sim/db' import { user, workflow, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import type { WorkflowState } from '@sim/workflow-types/workflow' @@ -60,7 +61,7 @@ describe('createWorkflowWithState against PostgreSQL', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values([ + await insertWorkspaceFixture(db, [ { id: workspaceId, name: 'Import target', ownerId: userId, billedAccountUserId: userId }, { id: archivedWorkspaceId, @@ -78,7 +79,7 @@ describe('createWorkflowWithState against PostgreSQL', () => { resolve('test-results/create-workflow-with-state.json') await mkdir(dirname(reportPath), { recursive: true }) await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) - await db.delete(workspace).where(eq(workspace.ownerId, userId)) + await deleteWorkspaceFixture(db, eq(workspace.ownerId, userId)) await db.delete(user).where(eq(user.id, userId)) }) From 78b6b7143fa497d342c785f0003e3334e20fad87 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 6 Oct 2026 13:34:42 -0700 Subject: [PATCH 030/113] fix(files): resolve lifecycle and search review findings --- .../handlers/file-doc-project.integration.ts | 95 +++++ apps/realtime/src/handlers/file-doc.ts | 11 +- .../api/files/public/[token]/content/route.ts | 4 +- .../app/api/files/serve/[...path]/route.ts | 1 + apps/sim/background/cleanup-soft-deletes.ts | 14 +- .../lib/billing/cleanup-dispatcher.test.ts | 48 +++ apps/sim/lib/billing/cleanup-dispatcher.ts | 80 ++--- apps/sim/lib/core/outbox/processor.test.ts | 1 + apps/sim/lib/core/outbox/processor.ts | 14 +- apps/sim/lib/core/outbox/service.ts | 4 +- .../private-purge.integration.ts | 246 ++++++++++++- .../__integration__/retention.integration.ts | 19 +- apps/sim/lib/projects/files/prefix-cleanup.ts | 340 +++++++++++++++--- apps/sim/lib/uploads/documents/compile.ts | 35 +- .../lib/uploads/documents/servable.test.ts | 34 ++ .../lib/uploads/utils/file-utils.server.ts | 1 + apps/sim/lib/users/account-deletion.ts | 10 +- .../sim/lib/workspace-files/ownership.test.ts | 15 +- apps/sim/lib/workspace-files/ownership.ts | 1 + .../search/dispatcher.integration.ts | 127 ++++++- .../lib/workspace-files/search/dispatcher.ts | 37 +- .../db/file-entity-ownership.integration.ts | 12 +- .../migrations/0398_file_entity_ownership.sql | 4 +- .../0403_file_search_owner_scope.sql | 34 ++ 24 files changed, 1014 insertions(+), 173 deletions(-) diff --git a/apps/realtime/src/handlers/file-doc-project.integration.ts b/apps/realtime/src/handlers/file-doc-project.integration.ts index 003ef7b9cc4..690d6989cf5 100644 --- a/apps/realtime/src/handlers/file-doc-project.integration.ts +++ b/apps/realtime/src/handlers/file-doc-project.integration.ts @@ -1,8 +1,13 @@ import { mkdir, writeFile } from 'node:fs/promises' import { createServer, type Server as HttpServer } from 'node:http' import { dirname, resolve } from 'node:path' +import { + ROOM_ACCESS_REVOKED_EVENT, + type RoomAccessRevokedBroadcast, +} from '@sim/realtime-protocol/events' import { FILE_DOC_EVENTS, + FILE_DOC_MESSAGE_TYPE, FILE_DOC_SCHEMA_VERSION, FILE_DOC_SEED, type JoinFileDocError, @@ -12,9 +17,11 @@ import { import { createDeferred } from '@sim/testing/helpers/deferred' import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' +import * as encoding from 'lib0/encoding' import { Server } from 'socket.io' import { io as connect, type Socket } from 'socket.io-client' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' +import * as syncProtocol from 'y-protocols/sync' import * as Y from 'yjs' import { startAccessRevalidationSweep } from '@/access-revalidation' import { env } from '@/env' @@ -447,6 +454,94 @@ describe('Project documents across the Socket.IO and internal HTTP boundary', () } ) + for (const status of [403, 404, 503]) { + check(`a reader frame resolves ${status} without waiting for an access sweep`, async () => { + const actor = `message-reader-${generateId()}` + const writerActor = `message-writer-${generateId()}` + actors.set(actor, 'read') + actors.set(writerActor, 'write') + const reader = await join(actor) + const writer = await join(writerActor) + const server = io.sockets.sockets.get(reader.socket.id ?? '') + if (!server) throw new Error('Admitted reader socket missing') + const originalMessage = server.listeners(FILE_DOC_EVENTS.MESSAGE)[0] + if (!originalMessage) throw new Error('Document message handler missing') + const handled = createDeferred() + const observeMessage = async (payload: unknown) => { + try { + await originalMessage.call(server, payload) + } finally { + handled.resolve() + } + } + server.off(FILE_DOC_EVENTS.MESSAGE, originalMessage) + server.on(FILE_DOC_EVENTS.MESSAGE, observeMessage) + const drainReader = async () => { + const drained = new Promise((resolve) => + reader.socket.once('fixture-drained', resolve) + ) + server.emit('fixture-drained') + await drained + } + await drainReader() + const frames: string[] = [] + const revocations: RoomAccessRevokedBroadcast[] = [] + reader.socket.on(FILE_DOC_EVENTS.MESSAGE, () => frames.push('document')) + reader.socket.on(FILE_DOC_EVENTS.PRESENCE, () => frames.push('presence')) + reader.socket.on(ROOM_ACCESS_REVOKED_EVENT, (payload) => revocations.push(payload)) + const gate = { + requests: 1, + entered: createDeferred(), + release: createDeferred(), + status, + } + gate.release.resolve() + accessGates.set(actor, gate) + const now = Date.now() + const clock = vi.spyOn(Date, 'now').mockReturnValue(now + ROLE_REVALIDATION_TTL_MS + 1) + const edit = new Y.Doc() + try { + const encoder = encoding.createEncoder() + encoding.writeVarUint(encoder, FILE_DOC_MESSAGE_TYPE.SYNC) + syncProtocol.writeSyncStep1(encoder, document) + reader.socket.emit(FILE_DOC_EVENTS.MESSAGE, encoding.toUint8Array(encoder)) + await handled.promise + await drainReader() + const denied = status !== 503 + expect(server.rooms.has(`project-file-doc:${projectId}/${fileId}`)).toBe(!denied) + expect(revocations).toHaveLength(denied ? 1 : 0) + if (denied) + expect(revocations[0].room).toEqual({ + type: 'project-file-doc', + id: `${projectId}/${fileId}`, + }) + Y.applyUpdate(edit, Buffer.from(seed, 'base64')) + edit.getText('body').insert(0, `after-message-${status}`) + expect( + await writer.socket.timeout(1500).emitWithAck(FILE_DOC_EVENTS.UPDATE, { + fileId, + projectId, + docId, + updateId: generateId(), + update: Y.encodeStateAsUpdate(edit), + }) + ).toMatchObject({ status: 'accepted' }) + await join('reader') + await drainReader() + if (denied) expect(frames).toEqual([]) + else expect(frames).toEqual(expect.arrayContaining(['document', 'presence'])) + } finally { + clock.mockRestore() + accessGates.delete(actor) + server.off(FILE_DOC_EVENTS.MESSAGE, observeMessage) + server.on(FILE_DOC_EVENTS.MESSAGE, originalMessage) + reader.socket.disconnect() + writer.socket.disconnect() + edit.destroy() + } + }) + } + check('a downgraded writer remains a reader but cannot submit another update', async () => { const writer = await join('writer') actors.set('writer', 'read') diff --git a/apps/realtime/src/handlers/file-doc.ts b/apps/realtime/src/handlers/file-doc.ts index 7cde577fd12..36e37d22f73 100644 --- a/apps/realtime/src/handlers/file-doc.ts +++ b/apps/realtime/src/handlers/file-doc.ts @@ -1252,7 +1252,16 @@ async function handleMessage(socket: AuthenticatedSocket, io: Server, data: unkn 'read', socket.id ) - if (!permission || socketToRoomName.get(socket.id) !== name) return + if (socketToRoomName.get(socket.id) !== name) return + if (permission === null) { + evictSocketFromRoom( + socket, + fileDocRoom(room.fileId, room.projectId), + 'Your access to this document has been revoked', + io + ) + return + } if (messageType === FILE_DOC_MESSAGE_TYPE.SYNC) { const peek = decoding.createDecoder(bytes) decoding.readVarUint(peek) diff --git a/apps/sim/app/api/files/public/[token]/content/route.ts b/apps/sim/app/api/files/public/[token]/content/route.ts index 6d7fa2648a4..06114034559 100644 --- a/apps/sim/app/api/files/public/[token]/content/route.ts +++ b/apps/sim/app/api/files/public/[token]/content/route.ts @@ -81,7 +81,9 @@ export const GET = withRouteHandler( }) const servable = file.workspaceId - ? await resolveServableDoc(file.workspaceId, raw, file.originalName) + ? await resolveServableDoc(file.workspaceId, raw, file.originalName, { + sourceMime: file.contentType, + }) : ({ kind: 'passthrough' } as const) if (servable.kind === 'unavailable') { diff --git a/apps/sim/app/api/files/serve/[...path]/route.ts b/apps/sim/app/api/files/serve/[...path]/route.ts index 1e545005156..bf7c925167e 100644 --- a/apps/sim/app/api/files/serve/[...path]/route.ts +++ b/apps/sim/app/api/files/serve/[...path]/route.ts @@ -189,6 +189,7 @@ async function resolveTransformedBytes(params: { const doc = await resolveServableDocBytes({ rawBuffer: buffer, fileName: filename, + sourceMime: fileType, workspaceId, filePrincipal, ownerKey, diff --git a/apps/sim/background/cleanup-soft-deletes.ts b/apps/sim/background/cleanup-soft-deletes.ts index c8e359fd819..de60086f94c 100644 --- a/apps/sim/background/cleanup-soft-deletes.ts +++ b/apps/sim/background/cleanup-soft-deletes.ts @@ -20,7 +20,6 @@ import { type CleanupJobPayload, runCleanupWithLimits } from '@/lib/billing/clea import { decrementStorageUsageForBillingContextInTx, resolveStorageBillingContext, - type StorageBillingContext, } from '@/lib/billing/storage' import { batchDeleteByWorkspaceAndTimestamp, @@ -326,22 +325,11 @@ async function deleteExpiredBillableWorkspaceFileRows( } for (const [workspaceId, workspaceRows] of rowsByWorkspace) { - let billingContext: StorageBillingContext - try { - billingContext = await resolveStorageBillingContext(workspaceId) - } catch (error) { - result.failed += workspaceRows.length - logger.error(`[${label}/workspaceFiles] Failed to resolve current storage payer`, { - error, - workspaceId, - }) - continue - } - for (const batch of chunkArray(workspaceRows, DEFAULT_DELETE_CHUNK_SIZE)) { try { const deletedCount = await db.transaction(async (tx) => { await lockWorkspaceProject(tx, workspaceId) + const billingContext = await resolveStorageBillingContext(workspaceId, tx) await releaseWorkspaceFileVersionsForPurgeInTx( tx, batch.map(({ id }) => id), diff --git a/apps/sim/lib/billing/cleanup-dispatcher.test.ts b/apps/sim/lib/billing/cleanup-dispatcher.test.ts index cd106b647cd..252fdbb2362 100644 --- a/apps/sim/lib/billing/cleanup-dispatcher.test.ts +++ b/apps/sim/lib/billing/cleanup-dispatcher.test.ts @@ -1,3 +1,5 @@ +import { flushMicrotasks } from '@sim/testing/helpers/async' +import { createDeferred } from '@sim/testing/helpers/deferred' import { asyncJobsMock, asyncJobsMockFns } from '@sim/testing/mocks/async-jobs.mock' import { asyncJobsRegionMock } from '@sim/testing/mocks/async-jobs-region.mock' import { billingCoreMock, billingCoreMockFns } from '@sim/testing/mocks/billing-core.mock' @@ -160,6 +162,52 @@ describe('organization-owned Search retention dispatch', () => { }) }) +describe('Project payer discovery backpressure', () => { + it('resolves multiple payers within a bounded window and skips a failed payer', async () => { + resetDbChainMock() + setEnvFlags({ isBillingEnabled: true, isDataRetentionEnabled: true }) + mockIsTriggerAvailable.mockReturnValue(false) + mockEnqueue.mockResolvedValue('cleanup-job') + queueTableRows(schemaMock.workspace, []) + queueTableRows( + schemaMock.project, + Array.from({ length: 30 }, (_, index) => ({ + id: `project-${index}`, + ownerId: `owner-${index}`, + organizationId: null, + settings: null, + })) + ) + queueTableRows(schemaMock.project, []) + const release = createDeferred() + let active = 0 + let peak = 0 + vi.mocked(getHighestPriorityPersonalSubscription).mockImplementation(async (userId) => { + active++ + peak = Math.max(peak, active) + try { + await release.promise + if (userId === 'owner-12') throw new Error('Payer unavailable') + return null + } finally { + active-- + } + }) + const dispatch = dispatchCleanupJobs('cleanup-file-versions') + try { + await flushMicrotasks(100) + expect(peak).toBeGreaterThan(1) + expect(peak).toBeLessThanOrEqual(10) + } finally { + release.resolve() + await dispatch + vi.mocked(getHighestPriorityPersonalSubscription).mockReset() + resetDbChainMock() + } + expect(await dispatch).toMatchObject({ chunkCount: 29, jobCount: 29 }) + }) +}) + describe('cleanup limits', () => { beforeEach(() => { resetDbChainMock() diff --git a/apps/sim/lib/billing/cleanup-dispatcher.ts b/apps/sim/lib/billing/cleanup-dispatcher.ts index 08a56cb0361..0998fd6551a 100644 --- a/apps/sim/lib/billing/cleanup-dispatcher.ts +++ b/apps/sim/lib/billing/cleanup-dispatcher.ts @@ -18,6 +18,7 @@ import { resolveTriggerRegion } from '@/lib/core/async-jobs/region' import type { EnqueueOptions } from '@/lib/core/async-jobs/types' import { isBillingEnabled, isDataRetentionEnabled } from '@/lib/core/config/env-flags' import { isTriggerAvailable } from '@/lib/core/config/trigger-availability' +import { mapWithConcurrency } from '@/lib/core/utils/concurrency' import { isOrganizationWorkspace, WORKSPACE_MODE } from '@/lib/workspaces/policy' const logger = createLogger('RetentionDispatcher') @@ -25,6 +26,7 @@ const logger = createLogger('RetentionDispatcher') /** Trigger.dev's documented cap on items per `batchTrigger` call (SDK 4.3.1+). */ const BATCH_TRIGGER_CHUNK_SIZE = 1000 const WORKSPACE_SCOPE_PAGE_SIZE = 500 +const PROJECT_PAYER_LOOKUP_CONCURRENCY = 10 /** Bounds per-run memory + DB connections regardless of plan size. */ const WORKSPACES_PER_CLEANUP_CHUNK = 500 @@ -348,50 +350,48 @@ async function forEachCleanupChunk( .limit(pageSize) if (!rows.length) break afterProjectId = rows[rows.length - 1].id - const groups = new Map< - string, - { plan: PlanCategory; retentionHours: number; projectIds: string[] } - >() - for (const row of rows) { - if (shouldStop()) break - try { - let plan: PlanCategory = 'enterprise' - if (isBillingEnabled) { - const billing = await resolveProjectStorageBillingContext({ + const scopes = await mapWithConcurrency( + rows, + PROJECT_PAYER_LOOKUP_CONCURRENCY, + async (row) => { + if (shouldStop()) return null + try { + let plan: PlanCategory = 'enterprise' + if (isBillingEnabled) { + const billing = await resolveProjectStorageBillingContext({ + projectId: row.id, + ownerId: row.ownerId, + organizationId: row.organizationId, + }) + if (row.organizationId && billing.plan === null) return null + plan = getPlanType(billing.plan) + } + const retentionHours = + plan === 'enterprise' ? (row.settings?.[config.key] ?? null) : config.defaults[plan] + if (retentionHours === null) return null + if (!Number.isFinite(retentionHours) || retentionHours < 0) + throw new Error('Invalid Project retention policy') + return { projectId: row.id, plan, retentionHours } + } catch (error) { + if (failOnLookupError) throw error + logger.error('Skipping Project cleanup after payer policy lookup failed', { projectId: row.id, - ownerId: row.ownerId, - organizationId: row.organizationId, + error, }) - if (row.organizationId && billing.plan === null) continue - plan = getPlanType(billing.plan) + return null } - const retentionHours = - plan === 'enterprise' ? (row.settings?.[config.key] ?? null) : config.defaults[plan] - if (retentionHours === null) continue - if (!Number.isFinite(retentionHours) || retentionHours < 0) - throw new Error('Invalid Project retention policy') - const key = `${plan}:${retentionHours}` - const group = groups.get(key) - if (group) group.projectIds.push(row.id) - else groups.set(key, { plan, retentionHours, projectIds: [row.id] }) - } catch (error) { - if (failOnLookupError) throw error - logger.error('Skipping Project cleanup after payer policy lookup failed', { - projectId: row.id, - error, - }) - } - } - for (const group of groups.values()) { - for (const projectId of group.projectIds) { - if (shouldStop()) break - await emitChunk({ - ...group, - projectIds: [projectId], - workspaceIds: [], - label: `${group.plan}/projects/${projectId}`, - }) } + ) + for (const scope of scopes) { + if (shouldStop()) break + if (!scope) continue + await emitChunk({ + plan: scope.plan, + retentionHours: scope.retentionHours, + projectIds: [scope.projectId], + workspaceIds: [], + label: `${scope.plan}/projects/${scope.projectId}`, + }) } } } diff --git a/apps/sim/lib/core/outbox/processor.test.ts b/apps/sim/lib/core/outbox/processor.test.ts index 3f9c9344b1c..5adeacd594e 100644 --- a/apps/sim/lib/core/outbox/processor.test.ts +++ b/apps/sim/lib/core/outbox/processor.test.ts @@ -48,6 +48,7 @@ vi.mock('@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' })) vi.mock('@/lib/projects/files/prefix-cleanup', () => ({ projectFilePrefixCleanupOutboxHandlers: {}, + recoverProjectStorageReconciliation: vi.fn(), })) vi.mock('@/lib/workflows/deployment-outbox', () => ({ workflowDeploymentOutboxHandlers: {} })) vi.mock('@/lib/workspaces/admin-move', () => ({ invitationMigrationOutboxHandlers: {} })) diff --git a/apps/sim/lib/core/outbox/processor.ts b/apps/sim/lib/core/outbox/processor.ts index 4a2870a8d60..73247f58fb1 100644 --- a/apps/sim/lib/core/outbox/processor.ts +++ b/apps/sim/lib/core/outbox/processor.ts @@ -22,7 +22,10 @@ import { recoverKnowledgeDocumentProcessing } from '@/lib/knowledge/documents/pr import { inboxCleanupOutboxHandlers } from '@/lib/mothership/inbox/cleanup-outbox' import { organizationResourceCleanupOutboxHandlers } from '@/lib/organizations/resource-cleanup' import { projectFileDocumentOutboxHandlers } from '@/lib/projects/files/application/document-lifecycle' -import { projectFilePrefixCleanupOutboxHandlers } from '@/lib/projects/files/prefix-cleanup' +import { + projectFilePrefixCleanupOutboxHandlers, + recoverProjectStorageReconciliation, +} from '@/lib/projects/files/prefix-cleanup' import { workspaceFileLiveDocOutboxHandlers } from '@/lib/uploads/contexts/workspace/workspace-file-live-doc-outbox' import { workspaceFileStorageCleanupOutboxHandlers } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' import { workflowDeploymentOutboxHandlers } from '@/lib/workflows/deployment-outbox' @@ -73,6 +76,15 @@ export async function runOutboxProcessor(): Promise { minRemainingMs: 95_000, }) + try { + if (Date.now() - startedAt < OUTBOX_PROCESSOR_RECOVERY_CUTOFF_MS) + await recoverProjectStorageReconciliation() + } catch (error) { + logger.error('Project storage reconciliation recovery failed', { + error: toError(error).message, + }) + } + let recoveredDocuments = 0 try { if (Date.now() - startedAt < OUTBOX_PROCESSOR_RECOVERY_CUTOFF_MS) { diff --git a/apps/sim/lib/core/outbox/service.ts b/apps/sim/lib/core/outbox/service.ts index 2467bc5a2b5..630d45fcbd7 100644 --- a/apps/sim/lib/core/outbox/service.ts +++ b/apps/sim/lib/core/outbox/service.ts @@ -86,8 +86,8 @@ export interface DeferredOutboxHandlerResult { * outbox row independently reaches completed or dead-letter, and for * bounded continuation after durable progress (`continueOutboxHandler`), * external polling with a separately persisted, finite poll allowance, or a - * retained storage tombstone whose successful bounded sweeps must recur because - * an empty listing cannot prove that in-flight provider writes have finished. + * periodic bounded storage inventory that discovers objects arriving after + * the finite retirement cleanup has completed. */ consumeAttempt?: boolean /** Reset consecutive failures only after a complete successful recurring sweep. */ diff --git a/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts b/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts index 99a5b46b388..5744ec07811 100644 --- a/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts +++ b/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts @@ -1,5 +1,5 @@ import { mkdtempSync } from 'node:fs' -import { mkdir, readFile, rm, writeFile } from 'node:fs/promises' +import { chmod, mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { db } from '@sim/db' @@ -360,7 +360,7 @@ describe('Private Project teardown and durable object cleanup', () => { }) check( - 'account deletion commits despite an object-store failure and its durable cleanup retries successfully', + 'account deletion leaves object deletion to durable cleanup and a failed worker can retry', async () => { const f = await fixture() const obstructed = join(storageRoot, f.keys[0]) @@ -371,8 +371,14 @@ describe('Private Project teardown and durable object cleanup', () => { expect(await db.select().from(project).where(eq(project.id, f.projectId))).toEqual([]) const events = await cleanupEvents(f.projectId) const failed = events.find((event) => (event.payload as { key?: string }).key === f.keys[0]) - expect(failed).toMatchObject({ status: 'pending', attempts: 1 }) + expect(events).toHaveLength(3) + expect(events.every((event) => event.status === 'pending' && event.attempts === 0)).toBe(true) if (!failed) throw new Error('Retryable cleanup missing') + expect( + await processOutboxEventById(failed.id, workspaceFileStorageCleanupOutboxHandlers) + ).toBe('pending') + const [attempted] = await db.select().from(outboxEvent).where(eq(outboxEvent.id, failed.id)) + expect(attempted).toMatchObject({ status: 'pending', attempts: 1 }) await rm(obstructed, { recursive: true }) await writeFile(obstructed, 'new') await db @@ -442,25 +448,34 @@ describe('Private Project teardown and durable object cleanup', () => { .set({ availableAt: new Date(0) }) .where(eq(outboxEvent.id, final.id)) expect(await processOutboxEventById(final.id, projectFilePrefixCleanupOutboxHandlers)).toBe( - 'pending' + 'completed' ) await expect(readFile(join(storageRoot, upload.finalKey))).rejects.toMatchObject({ code: 'ENOENT', }) + await expect(readdir(join(storageRoot, `project/${f.projectId}`))).rejects.toMatchObject({ + code: 'ENOENT', + }) + await mkdir(dirname(join(storageRoot, upload.finalKey)), { recursive: true }) await writeFile(join(storageRoot, upload.finalKey), 'finished-after-empty-sweep') + const reconciliationId = 'project-file.storage.reconcile' await db .update(outboxEvent) .set({ availableAt: new Date(0), attempts: 4 }) - .where(eq(outboxEvent.id, final.id)) - expect(await processOutboxEventById(final.id, projectFilePrefixCleanupOutboxHandlers)).toBe( - 'pending' - ) + .where(eq(outboxEvent.id, reconciliationId)) + expect( + await processOutboxEventById(reconciliationId, projectFilePrefixCleanupOutboxHandlers) + ).toBe('pending') await expect(readFile(join(storageRoot, upload.finalKey))).rejects.toMatchObject({ code: 'ENOENT', }) - const [retained] = await db.select().from(outboxEvent).where(eq(outboxEvent.id, final.id)) - expect(retained.attempts).toBe(0) - expect(retained.availableAt.getTime()).toBeGreaterThan(Date.now()) + expect(await readFile(join(storageRoot, live.keys[0]), 'utf8')).toBe('new') + const [reconciled] = await db + .select() + .from(outboxEvent) + .where(eq(outboxEvent.id, reconciliationId)) + expect(reconciled.attempts).toBe(0) + expect(reconciled.availableAt.getTime()).toBeGreaterThan(Date.now()) const unsafe = generateId() await db.insert(outboxEvent).values({ id: unsafe, @@ -528,6 +543,214 @@ describe('Private Project teardown and durable object cleanup', () => { } ) + check('retirement batches finish while sharing one inventory reconciliation event', async () => { + const f = await fixture() + await Promise.all( + Array.from({ length: 501 }, (_, index) => seedUpload(f, `batch-${index}.bin`)) + ) + await erasePrivateProject(f) + const empty = await fixture() + await erasePrivateProject(empty) + const events = await db + .select() + .from(outboxEvent) + .where( + and( + eq(outboxEvent.eventType, 'project-file.storage.prefix-cleanup'), + inArray(sql`${outboxEvent.payload}->>'projectId'`, [f.projectId, empty.projectId]) + ) + ) + expect(events).toHaveLength(6) + const { projectFilePrefixCleanupOutboxHandlers } = await import( + '@/lib/projects/files/prefix-cleanup' + ) + for (const event of events) { + await db + .update(outboxEvent) + .set({ availableAt: new Date(0) }) + .where(eq(outboxEvent.id, event.id)) + expect(await processOutboxEventById(event.id, projectFilePrefixCleanupOutboxHandlers)).toBe( + 'completed' + ) + } + const remaining = await db + .select() + .from(outboxEvent) + .where( + inArray( + outboxEvent.id, + events.map((event) => event.id) + ) + ) + expect(remaining.every((event) => event.status === 'completed')).toBe(true) + const reconciliations = await db + .select() + .from(outboxEvent) + .where(eq(outboxEvent.eventType, 'project-file.storage.reconcile')) + expect(reconciliations).toHaveLength(1) + }) + + check( + 'retirement revives terminal reconciliation without replacing active progress', + async () => { + const initial = await fixture() + await erasePrivateProject(initial) + const reconciliationId = 'project-file.storage.reconcile' + for (const status of ['completed', 'dead_letter', 'pending', 'processing']) { + const f = await fixture() + const lockedAt = new Date() + await db + .update(outboxEvent) + .set({ status, attempts: 4, payload: { cursor: 'saved-progress' }, lockedAt }) + .where(eq(outboxEvent.id, reconciliationId)) + await erasePrivateProject(f) + const [event] = await db + .select() + .from(outboxEvent) + .where(eq(outboxEvent.id, reconciliationId)) + if (status === 'completed' || status === 'dead_letter') { + expect(event).toMatchObject({ + status: 'pending', + attempts: 0, + payload: {}, + lockedAt: null, + }) + } else { + expect(event).toMatchObject({ + status, + attempts: 4, + payload: { cursor: 'saved-progress' }, + lockedAt, + }) + } + } + await db + .update(outboxEvent) + .set({ status: 'pending', attempts: 0, payload: {}, lockedAt: null }) + .where(eq(outboxEvent.id, reconciliationId)) + } + ) + + check( + 'inventory reconciliation isolates a failed prefix and retries it next cycle while preserving live owners', + async () => { + const retired = await fixture() + await erasePrivateProject(retired) + const live = await fixture() + const reconciliationId = 'project-file.storage.reconcile' + const orphanIds = Array.from({ length: 505 }, () => generateId()).sort() + const obstructed = join(storageRoot, `project/${orphanIds[0]}`) + for (const id of orphanIds) { + const prefix = join(storageRoot, `project/${id}`) + await mkdir(prefix, { recursive: true }) + await writeFile(join(prefix, 'late.bin'), 'late') + } + await chmod(obstructed, 0o000) + const { projectFilePrefixCleanupOutboxHandlers } = await import( + '@/lib/projects/files/prefix-cleanup' + ) + try { + await db + .update(outboxEvent) + .set({ availableAt: new Date(0), payload: {}, attempts: 3 }) + .where(eq(outboxEvent.id, reconciliationId)) + expect( + await processOutboxEventById(reconciliationId, projectFilePrefixCleanupOutboxHandlers) + ).toBe('pending') + const [failed] = await db + .select() + .from(outboxEvent) + .where(eq(outboxEvent.id, reconciliationId)) + expect(failed.attempts).toBe(3) + expect(failed.payload).toMatchObject({ failedPrefixes: 1, cursor: expect.any(String) }) + let completedCycle = false + for (let attempt = 0; attempt < 10; attempt++) { + await db + .update(outboxEvent) + .set({ availableAt: new Date(0) }) + .where(eq(outboxEvent.id, reconciliationId)) + expect( + await processOutboxEventById(reconciliationId, projectFilePrefixCleanupOutboxHandlers) + ).toBe('pending') + const [continued] = await db + .select() + .from(outboxEvent) + .where(eq(outboxEvent.id, reconciliationId)) + if (continued.availableAt.getTime() > Date.now() + 60 * 60 * 1000) { + expect(continued.attempts).toBe(3) + completedCycle = true + break + } + expect(continued.payload).toMatchObject({ cursor: expect.any(String) }) + } + expect(completedCycle).toBe(true) + for (const id of orphanIds.slice(1)) + await expect(readdir(join(storageRoot, `project/${id}`))).rejects.toMatchObject({ + code: 'ENOENT', + }) + expect(await readFile(join(storageRoot, live.keys[0]), 'utf8')).toBe('new') + } finally { + await chmod(obstructed, 0o755) + } + expect(await readFile(join(obstructed, 'late.bin'), 'utf8')).toBe('late') + await db + .update(outboxEvent) + .set({ availableAt: new Date(0) }) + .where(eq(outboxEvent.id, reconciliationId)) + expect( + await processOutboxEventById(reconciliationId, projectFilePrefixCleanupOutboxHandlers) + ).toBe('pending') + await expect(readdir(obstructed)).rejects.toMatchObject({ code: 'ENOENT' }) + const [recovered] = await db + .select() + .from(outboxEvent) + .where(eq(outboxEvent.id, reconciliationId)) + expect(recovered.attempts).toBe(0) + } + ) + + check( + 'periodic reconciliation recovery respects cooldown and active leases while preserving progress', + async () => { + const f = await fixture() + await erasePrivateProject(f) + const reconciliationId = 'project-file.storage.reconcile' + const { recoverProjectStorageReconciliation } = await import( + '@/lib/projects/files/prefix-cleanup' + ) + const now = new Date() + const checkpoint = { cursor: 'provider-page', page: [f.projectId], index: 0 } + const old = new Date(now.getTime() - 25 * 60 * 60 * 1000) + for (const status of ['pending', 'processing', 'dead_letter']) { + await db + .update(outboxEvent) + .set({ status, attempts: 10, payload: checkpoint, processedAt: old, lockedAt: now }) + .where(eq(outboxEvent.id, reconciliationId)) + await recoverProjectStorageReconciliation(now) + const [event] = await db + .select() + .from(outboxEvent) + .where(eq(outboxEvent.id, reconciliationId)) + expect(event.payload).toEqual(checkpoint) + expect(event).toMatchObject( + status === 'dead_letter' + ? { status: 'pending', attempts: 0, lockedAt: null, processedAt: null } + : { status, attempts: 10, lockedAt: now, processedAt: old } + ) + } + await db + .update(outboxEvent) + .set({ status: 'dead_letter', attempts: 10, processedAt: now }) + .where(eq(outboxEvent.id, reconciliationId)) + await recoverProjectStorageReconciliation(now) + const [cooling] = await db + .select() + .from(outboxEvent) + .where(eq(outboxEvent.id, reconciliationId)) + expect(cooling).toMatchObject({ status: 'dead_letter', attempts: 10, payload: checkpoint }) + } + ) + check('prefix sweeps continue bounded batches without consuming failure retries', async () => { const f = await fixture() const prefix = join(storageRoot, `project/${f.projectId}`) @@ -585,6 +808,7 @@ afterAll(async () => { ) ) } + await db.delete(outboxEvent).where(eq(outboxEvent.id, 'project-file.storage.reconcile')) await rm(storageRoot, { recursive: true, force: true }) } finally { const reportPath = process.env.PROJECT_PURGE_REPORT_PATH diff --git a/apps/sim/lib/projects/files/__integration__/retention.integration.ts b/apps/sim/lib/projects/files/__integration__/retention.integration.ts index d56e17a04e2..a978d5c6296 100644 --- a/apps/sim/lib/projects/files/__integration__/retention.integration.ts +++ b/apps/sim/lib/projects/files/__integration__/retention.integration.ts @@ -30,6 +30,7 @@ vi.mock('@/lib/core/config/env-flags', () => envFlagsMock) import { createSessionPrincipal } from '@sim/testing/factories/principal.factory' import { type CleanupJobPayload, runCleanupWithLimits } from '@/lib/billing/cleanup-dispatcher' +import { changeWorkspaceStoragePayerInTx } from '@/lib/billing/storage/payer-transfer' import { createCleanupBudgets } from '@/lib/cleanup/limits' import { authorizeProject } from '@/lib/projects/application/authorization' import { projectOperations } from '@/lib/projects/application/operations' @@ -234,7 +235,7 @@ describe('Project file retention follows the current payer in PostgreSQL', () => ) check( - 'workspace archive purge waits on current Project authority before locking file/version rows or billing', + 'workspace archive purge waits on Project authority and charges the payer that a preceding transfer selected', async () => { const f = await fixture() const expired = await seedFile(f, 40 * 24, true, 'workspace') @@ -257,6 +258,12 @@ describe('Project file retention follows the current payer in PostgreSQL', () => const result = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) acquired.resolve(result[0].pid) await release.promise + await changeWorkspaceStoragePayerInTx(tx, { + workspaceId: f.workspaceId, + billedAccountUserId: f.creatorId, + organizationId: null, + expectedCurrentPayer: { billedAccountUserId: f.ownerId, organizationId: null }, + }) }) const authPid = await Promise.race([ acquired.promise, @@ -305,6 +312,16 @@ describe('Project file retention follows the current payer in PostgreSQL', () => .from(userStats) .where(eq(userStats.userId, f.ownerId)) expect(usage.bytes).toBe(10) + const [destination] = await db + .select({ bytes: userStats.storageUsedBytes }) + .from(userStats) + .where(eq(userStats.userId, f.creatorId)) + expect(destination.bytes).toBe(0) + const [environment] = await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, f.workspaceId)) + expect(environment.bytes).toBe(0) } ) diff --git a/apps/sim/lib/projects/files/prefix-cleanup.ts b/apps/sim/lib/projects/files/prefix-cleanup.ts index cc1175b03fe..08f528420bd 100644 --- a/apps/sim/lib/projects/files/prefix-cleanup.ts +++ b/apps/sim/lib/projects/files/prefix-cleanup.ts @@ -1,14 +1,17 @@ -import { opendir } from 'node:fs/promises' -import { join } from 'node:path' +import { opendir, rmdir } from 'node:fs/promises' +import { dirname, join } from 'node:path' import { db } from '@sim/db' -import { project, uploadSession } from '@sim/db/schema' +import { outboxEvent, project, uploadSession } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { describeError } from '@sim/utils/errors' import { isRecordLike } from '@sim/utils/object' -import { and, asc, eq, gt, inArray, isNull, sql } from 'drizzle-orm' +import { compareStrings } from '@sim/utils/string' +import { and, asc, eq, gt, inArray, isNull, lte, sql } from 'drizzle-orm' import { continueOutboxHandler, deferOutboxHandler, enqueueOutboxEvent, + type OutboxEventContext, type OutboxHandler, type OutboxHandlerRegistry, } from '@/lib/core/outbox/service' @@ -27,9 +30,11 @@ import type { UploadSessionRecord } from '@/lib/uploads/upload-session/service' import { PROJECT_FILE_UPLOAD_BINDING_KEY } from '@/lib/uploads/upload-session/types' const EVENT_TYPE = 'project-file.storage.prefix-cleanup' +const RECONCILE_EVENT_TYPE = 'project-file.storage.reconcile' const PAGE_SIZE = 500 const CLOCK_SKEW_MS = 60_000 -const RETIRED_PREFIX_SWEEP_INTERVAL_MS = 24 * 60 * 60 * 1000 +const RECONCILE_INTERVAL_MS = 24 * 60 * 60 * 1000 +const logger = createLogger('ProjectStorageCleanup') type UploadCleanup = Pick< UploadSessionRecord, @@ -38,10 +43,40 @@ type UploadCleanup = Pick< interface CleanupPayload { projectId: string uploads?: UploadCleanup[] - retained?: boolean uploadCursor?: number } +interface ReconcilePayload { + cursor: string | null + page: string[] + index: number + unfinished: string[] + nextCursor: string | null + failedPrefixes: number +} + +/** Resume an existing inventory after its provider-outage retry budget and cooldown expire. */ +export async function recoverProjectStorageReconciliation(now = new Date()): Promise { + await db + .update(outboxEvent) + .set({ + status: 'pending', + attempts: 0, + lockedAt: null, + lastError: null, + processedAt: null, + availableAt: now, + }) + .where( + and( + eq(outboxEvent.id, RECONCILE_EVENT_TYPE), + eq(outboxEvent.eventType, RECONCILE_EVENT_TYPE), + eq(outboxEvent.status, 'dead_letter'), + lte(outboxEvent.processedAt, new Date(now.getTime() - RECONCILE_INTERVAL_MS)) + ) + ) +} + /** Expired signatures stop new transfers, but a transfer already in progress may finish later. */ function projectUploadCleanupAvailableAt(now = new Date()): Date { return new Date(now.getTime() + UPLOAD_URL_TTL_MS + CLOCK_SKEW_MS) @@ -56,7 +91,6 @@ function parsePayload(value: unknown): CleanupPayload { if (!isRecordLike(value) || typeof value.projectId !== 'string') throw new Error('Invalid Project cleanup payload') const prefix = prefixFor(value.projectId) - const retained = value.retained === true const uploadCursor = value.uploadCursor === undefined ? 0 : value.uploadCursor if ( !Number.isSafeInteger(uploadCursor) || @@ -65,7 +99,7 @@ function parsePayload(value: unknown): CleanupPayload { uploadCursor > PAGE_SIZE ) throw new Error('Invalid retired upload cursor') - if (value.uploads === undefined) return { projectId: value.projectId, retained, uploadCursor } + if (value.uploads === undefined) return { projectId: value.projectId, uploadCursor } if (!Array.isArray(value.uploads) || value.uploads.length > PAGE_SIZE) throw new Error('Invalid upload cleanup batch') const uploads = value.uploads.map((upload): UploadCleanup => { @@ -91,7 +125,7 @@ function parsePayload(value: unknown): CleanupPayload { finalKey: upload.finalKey, } }) - return { projectId: value.projectId, uploads, retained, uploadCursor } + return { projectId: value.projectId, uploads, uploadCursor } } async function enqueueSweeps( @@ -100,14 +134,30 @@ async function enqueueSweeps( retiredAt = new Date() ) { await enqueueOutboxEvent(executor, EVENT_TYPE, payload) - await enqueueOutboxEvent( - executor, - EVENT_TYPE, - { ...payload, retained: true }, - { + await enqueueOutboxEvent(executor, EVENT_TYPE, payload, { + availableAt: projectUploadCleanupAvailableAt(retiredAt), + }) + await executor + .insert(outboxEvent) + .values({ + id: RECONCILE_EVENT_TYPE, + eventType: RECONCILE_EVENT_TYPE, + payload: {}, availableAt: projectUploadCleanupAvailableAt(retiredAt), - } - ) + }) + .onConflictDoUpdate({ + target: outboxEvent.id, + set: { + status: 'pending', + payload: {}, + attempts: 0, + lockedAt: null, + lastError: null, + processedAt: null, + availableAt: projectUploadCleanupAvailableAt(retiredAt), + }, + setWhere: inArray(outboxEvent.status, ['completed', 'dead_letter']), + }) } /** Must follow Project and payer locks and precede directory/file locks in the retirement transaction. */ @@ -215,6 +265,14 @@ export async function queueRetiredProjectUploadCleanup( }) } +async function removeEmptyDirectory(path: string): Promise { + try { + await rmdir(path) + } catch (error) { + if (!['ENOENT', 'ENOTEMPTY', 'EEXIST'].includes(describeError(error).code ?? '')) throw error + } +} + async function localKeys(prefix: string, signal: AbortSignal): Promise { const keys: string[] = [] async function visit(key: string): Promise { @@ -233,6 +291,7 @@ async function localKeys(prefix: string, signal: AbortSignal): Promise else keys.push(child) if (keys.length >= PAGE_SIZE) return } + await removeEmptyDirectory(join(UPLOAD_DIR_SERVER, key)) } await visit(prefix) return keys @@ -274,6 +333,225 @@ async function listKeys(prefix: string, signal: AbortSignal): Promise return localKeys(prefix, signal) } +async function sweepPrefix(projectId: string, context: OutboxEventContext): Promise { + const prefix = prefixFor(projectId) + const keys = await listKeys(prefix, context.signal) + let deleted = 0 + for (const key of keys) { + if (!key.startsWith(prefix)) throw new Error('Storage provider returned a foreign Project key') + context.signal.throwIfAborted() + if (deleted > 0 && context.deadlineAt && Date.now() + 5000 >= context.deadlineAt) return false + try { + await deleteFile({ key, context: 'project', signal: context.signal }) + } catch (error) { + if (describeError(error).code !== 'ENOENT') throw error + } + if (!USE_S3_STORAGE && !USE_BLOB_STORAGE && !USE_GCS_STORAGE) { + const root = join(UPLOAD_DIR_SERVER, 'project', projectId) + let directory = dirname(join(UPLOAD_DIR_SERVER, key)) + while (directory === root || directory.startsWith(`${root}/`)) { + await removeEmptyDirectory(directory) + directory = dirname(directory) + } + } + deleted++ + } + return keys.length < PAGE_SIZE +} + +function readProjectId(prefix: string): string { + const match = /^project\/([a-zA-Z0-9_-]+)\/$/.exec(prefix) + if (!match) throw new Error('Storage provider returned an invalid Project prefix') + return match[1] +} + +async function listProjectPrefixes(cursor: string | null, signal: AbortSignal) { + signal.throwIfAborted() + if (USE_S3_STORAGE) { + const { ListObjectsV2Command } = await import('@aws-sdk/client-s3') + const { getS3Client } = await import('@/lib/uploads/providers/s3/client') + const page = await getS3Client().send( + new ListObjectsV2Command({ + Bucket: getStorageConfig('project').bucket, + Prefix: 'project/', + Delimiter: '/', + MaxKeys: PAGE_SIZE, + ...(cursor ? { ContinuationToken: cursor } : {}), + }), + { abortSignal: signal } + ) + return { + page: (page.CommonPrefixes ?? []).map((entry) => readProjectId(entry.Prefix ?? '')), + nextCursor: page.NextContinuationToken ?? null, + } + } + if (USE_BLOB_STORAGE) { + const { getBlobServiceClient } = await import('@/lib/uploads/providers/blob/client') + const config = getStorageConfig('project') + if (!config.containerName) throw new Error('Project blob container missing') + const pages = (await getBlobServiceClient()) + .getContainerClient(config.containerName) + .listBlobsByHierarchy('/', { + prefix: 'project/', + includeUncommitedBlobs: true, + abortSignal: signal, + }) + .byPage({ maxPageSize: PAGE_SIZE, ...(cursor ? { continuationToken: cursor } : {}) }) + const page = await pages.next() + return page.done + ? { page: [], nextCursor: null } + : { + page: (page.value.segment.blobPrefixes ?? []).map((entry) => readProjectId(entry.name)), + nextCursor: page.value.continuationToken || null, + } + } + if (USE_GCS_STORAGE) { + const { getGcsClient } = await import('@/lib/uploads/providers/google-cloud-storage/client') + const config = getStorageConfig('project') + if (!config.bucket) throw new Error('Project bucket missing') + const [, nextQuery, response] = await (await getGcsClient()).bucket(config.bucket).getFiles({ + prefix: 'project/', + delimiter: '/', + maxResults: PAGE_SIZE, + autoPaginate: false, + ...(cursor ? { pageToken: cursor } : {}), + }) + signal.throwIfAborted() + if ( + !isRecordLike(response) || + (response.prefixes !== undefined && !Array.isArray(response.prefixes)) + ) + throw new Error('Invalid Project storage listing') + const prefixes: unknown[] = response.prefixes ?? [] + return { + page: prefixes.map((prefix) => { + if (typeof prefix !== 'string') throw new Error('Invalid Project storage prefix') + return readProjectId(prefix) + }), + nextCursor: nextQuery?.pageToken ?? null, + } + } + let directory + try { + directory = await opendir(join(UPLOAD_DIR_SERVER, 'project')) + } catch (error) { + if (describeError(error).code === 'ENOENT') return { page: [], nextCursor: null } + throw error + } + const ids: string[] = [] + for await (const entry of directory) { + signal.throwIfAborted() + if (!entry.isDirectory() || (cursor && compareStrings(entry.name, cursor) <= 0)) continue + if (ids.length > PAGE_SIZE && compareStrings(entry.name, ids[ids.length - 1]) >= 0) continue + ids.push(readProjectId(`project/${entry.name}/`)) + ids.sort(compareStrings) + if (ids.length > PAGE_SIZE + 1) ids.pop() + } + const page = ids.slice(0, PAGE_SIZE) + return { page, nextCursor: ids.length > PAGE_SIZE ? page[page.length - 1] : null } +} + +function parseReconcilePayload(raw: unknown): ReconcilePayload { + if (!isRecordLike(raw)) throw new Error('Invalid Project reconciliation payload') + const cursor = raw.cursor ?? null + const nextCursor = raw.nextCursor ?? null + const page = raw.page ?? [] + const unfinished = raw.unfinished ?? [] + const index = raw.index ?? 0 + const failedPrefixes = raw.failedPrefixes ?? 0 + if ( + (cursor !== null && typeof cursor !== 'string') || + (nextCursor !== null && typeof nextCursor !== 'string') || + !Array.isArray(page) || + page.length > PAGE_SIZE || + !Array.isArray(unfinished) || + unfinished.length > PAGE_SIZE || + typeof index !== 'number' || + !Number.isSafeInteger(index) || + index < 0 || + index > page.length || + typeof failedPrefixes !== 'number' || + !Number.isSafeInteger(failedPrefixes) || + failedPrefixes < 0 + ) + throw new Error('Invalid Project reconciliation checkpoint') + function ownerIds(values: unknown[]): string[] { + return values.map((id) => { + if (typeof id !== 'string') throw new Error('Invalid Project reconciliation identity') + prefixFor(id) + return id + }) + } + return { + cursor, + nextCursor, + page: ownerIds(page), + unfinished: ownerIds(unfinished), + index, + failedPrefixes, + } +} + +const reconcilePrefixes: OutboxHandler = async (raw, context) => { + const payload = parseReconcilePayload(raw) + if (!payload.page.length) { + Object.assign(payload, await listProjectPrefixes(payload.cursor, context.signal), { + index: 0, + unfinished: [], + }) + await context.checkpointPayload({ ...payload }) + } + const owners = payload.page.length + ? await db.select({ id: project.id }).from(project).where(inArray(project.id, payload.page)) + : [] + const existing = new Set(owners.map((owner) => owner.id)) + for (let index = payload.index; index < payload.page.length; index++) { + context.signal.throwIfAborted() + const id = payload.page[index] + if (!existing.has(id)) { + try { + if (!(await sweepPrefix(id, context))) payload.unfinished.push(id) + } catch (error) { + context.signal.throwIfAborted() + payload.failedPrefixes++ + logger.warn('Project storage prefix cleanup failed; retrying on the next inventory', { + projectId: id, + error, + }) + } + } + payload.index = index + 1 + await context.checkpointPayload({ + index: payload.index, + unfinished: payload.unfinished, + failedPrefixes: payload.failedPrefixes, + }) + if (context.deadlineAt && Date.now() + 5000 >= context.deadlineAt) + return continueOutboxHandler('Continuing Project storage reconciliation') + } + if (payload.unfinished.length) { + await context.checkpointPayload({ page: payload.unfinished, index: 0, unfinished: [] }) + return continueOutboxHandler('Continuing partially cleaned Project prefixes') + } + await context.checkpointPayload({ + cursor: payload.nextCursor, + nextCursor: null, + page: [], + index: 0, + unfinished: [], + failedPrefixes: payload.nextCursor ? payload.failedPrefixes : 0, + }) + if (payload.nextCursor) return continueOutboxHandler('Continuing Project storage inventory') + return { + ...deferOutboxHandler( + `Project storage inventory reconciled with ${payload.failedPrefixes} failed prefixes`, + RECONCILE_INTERVAL_MS, + false + ), + resetAttempts: payload.failedPrefixes === 0, + } +} + const cleanupPrefix: OutboxHandler = async (raw, context) => { const payload = parsePayload(raw) const [owner] = await db @@ -282,7 +560,6 @@ const cleanupPrefix: OutboxHandler = async (raw, context) => { .where(eq(project.id, payload.projectId)) // Canonical creation always generates fresh Project IDs; an extant owner is never disposable. if (owner) throw new Error('Cannot clean storage for an existing Project') - const prefix = prefixFor(payload.projectId) const uploads = payload.uploads ?? [] for (let index = payload.uploadCursor ?? 0; index < uploads.length; index++) { const upload = uploads[index] @@ -312,34 +589,11 @@ const cleanupPrefix: OutboxHandler = async (raw, context) => { return continueOutboxHandler('Continuing retired Project upload cleanup') } } - const keys = await listKeys(prefix, context.signal) - let deleted = 0 - for (const key of keys) { - if (!key.startsWith(prefix)) throw new Error('Storage provider returned a foreign Project key') - context.signal.throwIfAborted() - if (deleted > 0 && context.deadlineAt && Date.now() + 5000 >= context.deadlineAt) - return continueOutboxHandler('Continuing Project storage cleanup') - try { - await deleteFile({ key, context: 'project', signal: context.signal }) - } catch (error) { - if (describeError(error).code !== 'ENOENT') throw error - } - deleted++ - } - if (keys.length >= PAGE_SIZE) return continueOutboxHandler('Continuing Project storage cleanup') - if (payload.retained) { - await context.checkpointPayload({ uploadCursor: 0 }) - return { - ...deferOutboxHandler( - 'Retired Project prefix remains subject to late transfer cleanup', - RETIRED_PREFIX_SWEEP_INTERVAL_MS, - false - ), - resetAttempts: true, - } - } + if (!(await sweepPrefix(payload.projectId, context))) + return continueOutboxHandler('Continuing Project storage cleanup') } export const projectFilePrefixCleanupOutboxHandlers = { [EVENT_TYPE]: cleanupPrefix, + [RECONCILE_EVENT_TYPE]: reconcilePrefixes, } satisfies OutboxHandlerRegistry diff --git a/apps/sim/lib/uploads/documents/compile.ts b/apps/sim/lib/uploads/documents/compile.ts index 3fd5159feae..becb6507afa 100644 --- a/apps/sim/lib/uploads/documents/compile.ts +++ b/apps/sim/lib/uploads/documents/compile.ts @@ -346,6 +346,7 @@ except Exception as __sim_recalc_err: interface LegacyCompileArgs { source: string fileName: string + sourceMime?: string workspaceId: string ownerKey?: string signal?: AbortSignal @@ -580,7 +581,7 @@ async function buildCompiledDoc( dependsOnReferencedFiles: touchesReferencedFiles( args.source, contributingFiles.length, - getDocumentSourceLanguage(args.source, fmt) + getDocumentSourceLanguage(args.source, fmt, args.sourceMime) ), ...(contributingFiles.length > 0 ? { contributingFiles } : {}), } @@ -729,12 +730,13 @@ async function compileDocInLegacySandbox( * artifact until broker calls can report the exact file versions they accessed. */ export async function compileDoc(args: CompileArgs): Promise { - const { source, fileName, workspaceId } = args + const { source, fileName, workspaceId, sourceMime } = args const fmt = await getE2BDocFormat(fileName) if (!fmt) throw new Error(`Unsupported document format: ${fileName}`) if (!isDocSandboxEnabled) return compileDocInLegacySandbox(args, fmt) - const referencedFileIds = collectReferencedFileIds(source, getDocumentSourceLanguage(source, fmt)) + const sourceLanguage = getDocumentSourceLanguage(source, fmt, sourceMime) + const referencedFileIds = collectReferencedFileIds(source, sourceLanguage) const referencedImages = await resolveReferencedImages( workspaceId, args.filePrincipal, @@ -769,7 +771,7 @@ export async function compileDoc(args: CompileArgs): Promise dependsOnReferencedFiles: touchesReferencedFiles( source, contributingFiles.length, - getDocumentSourceLanguage(source, fmt) + sourceLanguage ), ...(contributingFiles.length > 0 ? { contributingFiles } : {}), } @@ -790,6 +792,7 @@ async function loadCompiledDocByExt( allowLegacyReferencedArtifact?: boolean allowPublishedReferencedArtifact?: boolean filePrincipal?: Principal + sourceMime?: string } = {} ): Promise<{ buffer: Buffer @@ -800,7 +803,10 @@ async function loadCompiledDocByExt( const fmt = await getE2BDocFormat(`x.${ext}`) if (!fmt) return null const readOptions: CompiledDocReadOptions = { maxBytes: options.maxBytes, signal: options.signal } - const referencedFileIds = collectReferencedFileIds(source, getDocumentSourceLanguage(source, fmt)) + const referencedFileIds = collectReferencedFileIds( + source, + getDocumentSourceLanguage(source, fmt, options.sourceMime) + ) if (!options.filePrincipal) { if (referencedFileIds.size === 0) { const buffer = await loadCompiledDoc(workspaceId, source, fmt.ext, undefined, readOptions) @@ -885,7 +891,7 @@ export async function resolveServableDoc( workspaceId: string, storedBytes: Buffer, fileName: string, - options: CompiledDocReadOptions = {} + options: CompiledDocReadOptions & { sourceMime?: string } = {} ): Promise { const fmt = await getE2BDocFormat(fileName) if (!fmt) return { kind: 'passthrough' } @@ -954,12 +960,13 @@ function compiledCacheSet( export async function resolveServableDocBytes(args: { rawBuffer: Buffer fileName: string + sourceMime?: string workspaceId: string | undefined filePrincipal?: Principal ownerKey?: string signal?: AbortSignal }): Promise { - const { rawBuffer, fileName, workspaceId, filePrincipal, ownerKey, signal } = args + const { rawBuffer, fileName, sourceMime, workspaceId, filePrincipal, ownerKey, signal } = args const ext = fileName.slice(fileName.lastIndexOf('.')).toLowerCase() const extNoDot = ext.replace(/^\./, '') const format = COMPILABLE_FORMATS[ext] @@ -998,23 +1005,33 @@ export async function resolveServableDocBytes(args: { } const referencedFileIds = collectReferencedFileIds( source, - getDocumentSourceLanguage(source, fmt) + getDocumentSourceLanguage(source, fmt, sourceMime) ) if (referencedFileIds.size > 0) { if (!filePrincipal) { const published = await loadCompiledDocByExt(workspaceId, source, extNoDot, { allowPublishedReferencedArtifact: true, + sourceMime, }) if (published) return { ...published, dependsOnReferencedFiles: true } throw new Error( 'Referenced document resolution requires an authorized workspace file principal' ) } - return compileDoc({ source, fileName, workspaceId, filePrincipal, ownerKey, signal }) + return compileDoc({ + source, + fileName, + sourceMime, + workspaceId, + filePrincipal, + ownerKey, + signal, + }) } const stored = await loadCompiledDocByExt(workspaceId, source, extNoDot, { allowLegacyReferencedArtifact: true, filePrincipal, + sourceMime, }) // Reached only where the source references nothing, so the artifact is keyed by the source // alone and cannot change while that source does not. diff --git a/apps/sim/lib/uploads/documents/servable.test.ts b/apps/sim/lib/uploads/documents/servable.test.ts index a215fbbc28d..e1c8eb98bdd 100644 --- a/apps/sim/lib/uploads/documents/servable.test.ts +++ b/apps/sim/lib/uploads/documents/servable.test.ts @@ -72,6 +72,40 @@ describe('resolveServableDocBytes', () => { mockLoadPublishedCompiledDoc.mockResolvedValue(null) }) + it.each(['authenticated', 'public', 'compile'] as const)( + 'serves a retained JavaScript PDF through %s resolution using its source MIME', + async (surface) => { + const source = "const note = `It's fine`;\n// getFileBase64('example-only')\npdf.addPage();" + const artifact = Buffer.from('%PDF-retained-javascript') + mockLoadCompiledDoc.mockResolvedValue(artifact) + + const resolved = + surface === 'public' + ? await resolveServableDoc(WORKSPACE_ID, Buffer.from(source), 'retained.pdf', { + sourceMime: 'text/x-pdflibjs', + }) + : surface === 'compile' + ? await compileDoc({ + source, + fileName: 'retained.pdf', + workspaceId: WORKSPACE_ID, + filePrincipal: FILE_PRINCIPAL, + sourceMime: 'text/x-pdflibjs', + }) + : await resolveServableDocBytes({ + rawBuffer: Buffer.from(source), + fileName: 'retained.pdf', + workspaceId: WORKSPACE_ID, + filePrincipal: FILE_PRINCIPAL, + sourceMime: 'text/x-pdflibjs', + }) + + expect(resolved).toMatchObject({ buffer: artifact, contentType: 'application/pdf' }) + if ('dependsOnReferencedFiles' in resolved) + expect(resolved.dependsOnReferencedFiles).toBe(false) + } + ) + it('swaps generated-doc source for the compiled artifact + binary content type', async () => { const artifact = Buffer.from('%PDF-compiled-binary') mockLoadCompiledDoc.mockResolvedValue(artifact) diff --git a/apps/sim/lib/uploads/utils/file-utils.server.ts b/apps/sim/lib/uploads/utils/file-utils.server.ts index 8d3aff25dbb..f266129659a 100644 --- a/apps/sim/lib/uploads/utils/file-utils.server.ts +++ b/apps/sim/lib/uploads/utils/file-utils.server.ts @@ -496,6 +496,7 @@ export async function downloadServableFileFromStorage( const resolved = await resolveServableDocBytes({ rawBuffer: buffer, fileName: userFile.name, + sourceMime: userFile.type, workspaceId, filePrincipal: options.filePrincipal, ownerKey: options.ownerKey, diff --git a/apps/sim/lib/users/account-deletion.ts b/apps/sim/lib/users/account-deletion.ts index 3de29b59eab..f748152778f 100644 --- a/apps/sim/lib/users/account-deletion.ts +++ b/apps/sim/lib/users/account-deletion.ts @@ -37,7 +37,6 @@ import { } from '@/lib/table/rows/executions' import type { StorageContext } from '@/lib/uploads' import { isUsingCloudStorage, StorageService } from '@/lib/uploads' -import { processWorkspaceFileStorageCleanupsNow } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' import { reassignBilledAccountForUser, reassignOwnedWorkspacesForUser, @@ -693,11 +692,9 @@ export async function deleteUserAccount(userId: string): Promise { - const projectCleanup = await prepareProjectsForAccountDeletion(tx, userId, doomedWorkspaceIds) - projectStorageCleanupEventIds = projectCleanup.storageCleanupEventIds + await prepareProjectsForAccountDeletion(tx, userId, doomedWorkspaceIds) if (doomedWorkspaceIds.length > 0) { /** * Re-checked here rather than trusted from the plan: a workspace that @@ -842,11 +839,6 @@ export async function deleteUserAccount(userId: string): Promise { - it('keeps audited workspace ownership independent of Project support', () => { - expect(resolveFileOwner(legacyWorkspaceFile)).toEqual({ - entityType: 'workspace', - entityId: 'workspace-a', - }) - }) + it.each(['workspace', 'chat'])( + 'preserves the explicit workspace owner of a legacy %s file', + (context) => { + expect(resolveFileOwner({ ...legacyWorkspaceFile, context })).toEqual({ + entityType: 'workspace', + entityId: 'workspace-a', + }) + } + ) it('does not change shared ownership when the uploader changes', () => { expect( diff --git a/apps/sim/lib/workspace-files/ownership.ts b/apps/sim/lib/workspace-files/ownership.ts index 7b4ac03aaa8..f61b3b78972 100644 --- a/apps/sim/lib/workspace-files/ownership.ts +++ b/apps/sim/lib/workspace-files/ownership.ts @@ -38,6 +38,7 @@ export function resolveFileOwner(file: PersistedFileOwnership): FileOwner | null if (file.workspaceId) { switch (file.context) { case 'workspace': + case 'chat': case 'mothership': case 'execution': case 'workspace-logos': diff --git a/apps/sim/lib/workspace-files/search/dispatcher.integration.ts b/apps/sim/lib/workspace-files/search/dispatcher.integration.ts index 33b14f1ceb5..4c34a78a575 100644 --- a/apps/sim/lib/workspace-files/search/dispatcher.integration.ts +++ b/apps/sim/lib/workspace-files/search/dispatcher.integration.ts @@ -31,6 +31,7 @@ import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { FILE_SEARCH_BACKFILL_PAGE_SIZE, FILE_SEARCH_DISPATCH_HANDOFF_MS, + FILE_SEARCH_INDEX_DISPATCH_WORKSPACES, FILE_SEARCH_INDEX_STALE_DISPATCH_MS, } from '@/lib/workspace-files/search/constants' import { @@ -56,6 +57,40 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { }, }) ) + const migration = readFileSync( + resolve(process.cwd(), '../../packages/db/migrations/0403_file_search_owner_scope.sql'), + 'utf8' + ).replaceAll('"public".', `"${schemaName}".`) + const ownerIndexes = [ + ['file_search_chunk_owner_content_idx', 'workspace_file_search_chunk'], + ['file_search_revision_owner_pending_idx', 'workspace_file_search_revision'], + ['file_search_revision_owner_status_idx', 'workspace_file_search_revision'], + ['workspace_files_search_owner_keyset_idx', 'workspace_files'], + ] as const + + async function migrateOwnerSearch() { + const session = await connection.reserve() + try { + await session`BEGIN` + for (const statement of migration.split('--> statement-breakpoint')) + if (statement.trim()) await session.unsafe(statement) + await session`COMMIT` + } finally { + await session`ROLLBACK` + await session`RESET lock_timeout` + session.release() + } + } + + async function readOwnerIndexes() { + return connection`SELECT c.relname, i.indisvalid, i.indisready, + pg_get_indexdef(i.indexrelid) AS definition, i.indexrelid::int AS oid + FROM pg_index i JOIN pg_class c ON c.oid = i.indexrelid + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = ${schemaName} + AND c.relname IN ${connection(ownerIndexes.map(([name]) => name))} + ORDER BY c.relname` + } beforeAll(async () => { await connection`CREATE SCHEMA ${connection(schemaName)}` @@ -99,28 +134,14 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { VALUES ('file-search-owners-v3', '2026-09-16 00:00:00')` await connection`CREATE TABLE workspace_file_search_build (id text PRIMARY KEY, workspace_id text NOT NULL, file_id text NOT NULL, expires_at timestamp)` await connection`CREATE TABLE workspace_file_search_chunk (build_id text NOT NULL, workspace_id text NOT NULL, content text NOT NULL, ordinal integer NOT NULL, PRIMARY KEY(build_id, ordinal))` - const migration = readFileSync( - resolve(process.cwd(), '../../packages/db/migrations/0403_file_search_owner_scope.sql'), - 'utf8' - ).replaceAll('"public".', `"${schemaName}".`) - const session = await connection.reserve() - try { - await session`BEGIN` - for (const statement of migration.split('--> statement-breakpoint')) - if (statement.trim()) await session.unsafe(statement) - await session`COMMIT` - } finally { - await session`ROLLBACK` - await session`RESET lock_timeout` - session.release() - } + await migrateOwnerSearch() database.current = drizzle(connection) }) beforeEach(async () => { mocks.batchTrigger.mockReset() await connection`DROP TRIGGER IF EXISTS slow_backfill ON workspace_file_search_backfill` - await connection`TRUNCATE workspace_files, workspace_file_search_revision, workspace_file_search_dispatch_queue, file_search_dispatch_queue` + await connection`TRUNCATE workspace_files, workspace_file_search_revision, workspace_file_search_dispatch_queue, file_search_dispatch_queue, workspace_file_search_build, workspace_file_search_chunk CASCADE` await connection`UPDATE workspace_file_search_backfill SET updated_at = '2026-09-16 00:00:00', completed_at = NULL, after_workspace_id = NULL, after_entity_type = NULL, after_entity_id = NULL, after_file_id = NULL` @@ -164,6 +185,80 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { (entity_type, entity_id, enqueued_at, updated_at) VALUES (${entityType}, ${workspaceId}, now(), now())` } + it.each(['original', 'recovery'] as const)( + 'rebuilds interrupted owner search indexes left under their %s names', + async (location) => { + await seedQueue('migration-replay', 2) + await connection`INSERT INTO workspace_file_search_build (id, workspace_id, file_id) + SELECT 'build-' || id, workspace_id, id FROM workspace_files` + await connection`INSERT INTO workspace_file_search_chunk (build_id, workspace_id, content, ordinal) + SELECT id, workspace_id, 'searchable', 0 FROM workspace_file_search_build` + const expected = (await readOwnerIndexes()).map(({ oid: _oid, ...index }) => index) + + for (const [name, table] of ownerIndexes) { + await connection`DROP INDEX CONCURRENTLY ${connection(name)}` + await expect( + connection`CREATE UNIQUE INDEX CONCURRENTLY ${connection(name)} ON ${connection(table)} ((1))` + ).rejects.toMatchObject({ code: '23505' }) + if (location === 'recovery') + await connection`ALTER INDEX ${connection(name)} RENAME TO ${connection(`${name}_failed_0403`)}` + } + const invalid = await connection`SELECT count(*)::int AS count FROM pg_index i + JOIN pg_class c ON c.oid = i.indexrelid JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = ${schemaName} AND NOT i.indisvalid` + expect(invalid[0].count).toBe(ownerIndexes.length) + + await migrateOwnerSearch() + + expect((await readOwnerIndexes()).map(({ oid: _oid, ...index }) => index)).toEqual(expected) + const remaining = await connection`SELECT count(*)::int AS count FROM pg_index i + JOIN pg_class c ON c.oid = i.indexrelid JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = ${schemaName} AND NOT i.indisvalid` + expect(remaining[0].count).toBe(0) + } + ) + + it('preserves healthy owner search indexes when replaying the migration', async () => { + const before = await readOwnerIndexes() + await migrateOwnerSearch() + expect(await readOwnerIndexes()).toEqual(before) + }) + + it('rotates a full batch of busy Projects so a later owner can dispatch', async () => { + const projects = Array.from( + { length: FILE_SEARCH_INDEX_DISPATCH_WORKSPACES }, + (_, index) => `busy-${String(index).padStart(3, '0')}` + ) + for (const id of projects) await seedQueue(id, 1, 0, 'project') + await seedQueue('later-workspace', 1) + const holder = await connection.reserve() + try { + await holder`BEGIN` + for (const id of projects) + await holder`SELECT pg_advisory_xact_lock(hashtextextended(${`project:${id}`}, 0))` + + const first = await prepareWorkspaceFileSearchDispatch() + const second = await prepareWorkspaceFileSearchDispatch() + expect([...first.payloads, ...second.payloads].map((payload) => payload.owner)).toEqual([ + { entityType: 'workspace', entityId: 'later-workspace' }, + ]) + const [queued] = + await connection`SELECT count(*)::int AS count FROM file_search_dispatch_queue + WHERE entity_type = 'project'` + expect(queued.count).toBe(projects.length) + + await holder`COMMIT` + const resumed = await prepareWorkspaceFileSearchDispatch() + expect(resumed.payloads.length).toBeGreaterThan(0) + expect(resumed.payloads.every((payload) => payload.owner?.entityType === 'project')).toBe( + true + ) + } finally { + await holder`ROLLBACK` + holder.release() + } + }) + it('leaves a busy Project queued without blocking unrelated workspace dispatch', async () => { await seedQueue('busy-project', 1, 0, 'project') await seedQueue('free-workspace', 1) diff --git a/apps/sim/lib/workspace-files/search/dispatcher.ts b/apps/sim/lib/workspace-files/search/dispatcher.ts index 2a4f8161027..a63f3abd0e5 100644 --- a/apps/sim/lib/workspace-files/search/dispatcher.ts +++ b/apps/sim/lib/workspace-files/search/dispatcher.ts @@ -17,7 +17,6 @@ import { and, asc, eq, - exists, inArray, isNotNull, isNull, @@ -159,6 +158,17 @@ function rowOwner(row: { entityType: string; entityId: string }): EditableFileOw return resolveFileSearchOwner({ owner: row }) } +function queuedOwnerFilter(owners: readonly EditableFileOwner[]): SQL | undefined { + return or( + ...owners.map((owner) => + and( + eq(fileSearchDispatchQueue.entityType, owner.entityType), + eq(fileSearchDispatchQueue.entityId, owner.entityId) + ) + ) + ) +} + async function enqueueOwners( tx: DbTransaction, owners: readonly EditableFileOwner[], @@ -483,19 +493,9 @@ async function claimQueuedOwnerJobs( ) ) ) - const selected = or( - ...owners.map((owner) => - and( - eq(fileSearchDispatchQueue.entityType, owner.entityType), - eq(fileSearchDispatchQueue.entityId, owner.entityId) - ) - ) - ) await tx - .update(fileSearchDispatchQueue) - .set({ lastDispatchedAt: now, updatedAt: now }) - .where(and(selected, exists(remaining))) - await tx.delete(fileSearchDispatchQueue).where(and(selected, notExists(remaining))) + .delete(fileSearchDispatchQueue) + .where(and(queuedOwnerFilter(owners), notExists(remaining))) return rows.map((row) => ({ owner: rowOwner(row), fileId: row.fileId, @@ -590,7 +590,16 @@ export async function prepareWorkspaceFileSearchDispatch( asc(fileSearchDispatchQueue.entityId) ) .limit(Math.min(FILE_SEARCH_INDEX_DISPATCH_WORKSPACES, remainingGlobalCapacity)) - const owners = await lockFileSearchOwners(tx, queued.map(rowOwner), { skipBusy: true }) + .for('update', { skipLocked: true }) + const candidates = queued.map(rowOwner) + if (candidates.length > 0) { + // Rotate busy owners too; otherwise a full locked page starves every later owner. + await tx + .update(fileSearchDispatchQueue) + .set({ lastDispatchedAt: now, updatedAt: now }) + .where(queuedOwnerFilter(candidates)) + } + const owners = await lockFileSearchOwners(tx, candidates, { skipBusy: true }) await reconcileOwnerDependencies(tx, owners, now) const payloads = await runDispatchPhase('claim', () => claimQueuedOwnerJobs(tx, owners, remainingGlobalCapacity, now) diff --git a/packages/db/file-entity-ownership.integration.ts b/packages/db/file-entity-ownership.integration.ts index 2fb8e792424..0583ee042db 100644 --- a/packages/db/file-entity-ownership.integration.ts +++ b/packages/db/file-entity-ownership.integration.ts @@ -113,14 +113,18 @@ describe('file entity ownership migration in PostgreSQL', () => { }) }) - it.each(['execution', 'workspace-logos', 'knowledge-base'])( + it.each(['chat', 'execution', 'workspace-logos', 'knowledge-base'])( 'maps legacy %s rows to their workspace', async (context) => { await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) VALUES ('file', ${context}, 'user-a', 'workspace-a')` - const [row] = - await sql`SELECT workspace_id, project_id FROM workspace_files WHERE id = 'file'` - expect(row).toEqual({ workspace_id: 'workspace-a', project_id: null }) + const [row] = await sql`SELECT owner.entity_type, owner.entity_id + FROM workspace_files file + CROSS JOIN LATERAL workspace_file_owner( + file.context, file.workspace_id, file.project_id, file.organization_id, file.user_id + ) owner + WHERE file.id = 'file'` + expect(row).toEqual({ entity_type: 'workspace', entity_id: 'workspace-a' }) } ) diff --git a/packages/db/migrations/0398_file_entity_ownership.sql b/packages/db/migrations/0398_file_entity_ownership.sql index f54ffb7d794..f67ceeb1cbb 100644 --- a/packages/db/migrations/0398_file_entity_ownership.sql +++ b/packages/db/migrations/0398_file_entity_ownership.sql @@ -21,7 +21,7 @@ LANGUAGE sql IMMUTABLE AS $$ WHEN file_project_id IS NOT NULL AND file_workspace_id IS NULL AND file_organization_id IS NULL AND file_context = 'project' THEN 'project' WHEN file_project_id IS NULL AND file_organization_id IS NULL AND file_workspace_id IS NOT NULL - AND file_context IN ('workspace', 'mothership', 'execution', 'workspace-logos', 'knowledge-base') + AND file_context IN ('workspace', 'chat', 'mothership', 'execution', 'workspace-logos', 'knowledge-base') THEN 'workspace' WHEN file_project_id IS NULL AND file_workspace_id IS NULL AND file_organization_id IS NOT NULL AND file_context = 'knowledge-base' THEN 'organization' @@ -32,7 +32,7 @@ LANGUAGE sql IMMUTABLE AS $$ WHEN file_project_id IS NOT NULL AND file_workspace_id IS NULL AND file_organization_id IS NULL AND file_context = 'project' THEN file_project_id WHEN file_project_id IS NULL AND file_organization_id IS NULL AND file_workspace_id IS NOT NULL - AND file_context IN ('workspace', 'mothership', 'execution', 'workspace-logos', 'knowledge-base') + AND file_context IN ('workspace', 'chat', 'mothership', 'execution', 'workspace-logos', 'knowledge-base') THEN file_workspace_id WHEN file_project_id IS NULL AND file_workspace_id IS NULL AND file_organization_id IS NOT NULL AND file_context = 'knowledge-base' THEN file_organization_id diff --git a/packages/db/migrations/0403_file_search_owner_scope.sql b/packages/db/migrations/0403_file_search_owner_scope.sql index 95ae86cc6e9..8859a9eaf24 100644 --- a/packages/db/migrations/0403_file_search_owner_scope.sql +++ b/packages/db/migrations/0403_file_search_owner_scope.sql @@ -162,16 +162,50 @@ BEGIN END; $$; --> statement-breakpoint +DO $$ +DECLARE target record; recovery_name text; target_table regclass; +BEGIN + FOR target IN SELECT * FROM (VALUES + ('file_search_chunk_owner_content_idx', 'workspace_file_search_chunk'), + ('file_search_revision_owner_pending_idx', 'workspace_file_search_revision'), + ('file_search_revision_owner_status_idx', 'workspace_file_search_revision'), + ('workspace_files_search_owner_keyset_idx', 'workspace_files') + ) AS indexes(index_name, table_name) LOOP + recovery_name := target.index_name || '_failed_0403'; + target_table := to_regclass(target.table_name); + IF EXISTS ( + SELECT 1 FROM pg_index WHERE indexrelid = to_regclass(recovery_name) + AND (indisvalid OR indrelid <> target_table) + ) THEN + RAISE EXCEPTION 'Refusing to drop unexpected recovery index %', recovery_name; + END IF; + IF EXISTS ( + SELECT 1 FROM pg_index WHERE indexrelid = to_regclass(target.index_name) + AND indrelid = target_table AND NOT indisvalid + ) THEN + EXECUTE format('ALTER INDEX %I RENAME TO %I', target.index_name, recovery_name); + END IF; + END LOOP; +END $$; +--> statement-breakpoint COMMIT; --> statement-breakpoint SET lock_timeout = 0; --> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "file_search_chunk_owner_content_idx_failed_0403"; +--> statement-breakpoint CREATE INDEX CONCURRENTLY IF NOT EXISTS "file_search_chunk_owner_content_idx" ON "workspace_file_search_chunk" USING gin ("entity_type" text_ops,"entity_id" text_ops,"content" gin_trgm_ops) WITH (fastupdate=off) WHERE "workspace_file_search_chunk"."entity_type" = 'project'; --> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "file_search_revision_owner_pending_idx_failed_0403"; +--> statement-breakpoint CREATE INDEX CONCURRENTLY IF NOT EXISTS "file_search_revision_owner_pending_idx" ON "workspace_file_search_revision" USING btree (coalesce("entity_type", 'workspace'),coalesce("entity_id", "workspace_id"),"updated_at","file_id","source_content_updated_at") WHERE "workspace_file_search_revision"."status" = 'pending' AND "workspace_file_search_revision"."dispatched_at" IS NULL; --> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "file_search_revision_owner_status_idx_failed_0403"; +--> statement-breakpoint CREATE INDEX CONCURRENTLY IF NOT EXISTS "file_search_revision_owner_status_idx" ON "workspace_file_search_revision" USING btree (coalesce("entity_type", 'workspace'),coalesce("entity_id", "workspace_id"),"status","dispatched_at"); --> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS "workspace_files_search_owner_keyset_idx_failed_0403"; +--> statement-breakpoint CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_search_owner_keyset_idx" ON "workspace_files" USING btree ((CASE WHEN "project_id" IS NOT NULL THEN 'project' ELSE 'workspace' END),coalesce("project_id", "workspace_id"),"id") WHERE "workspace_files"."deleted_at" IS NULL AND (("workspace_files"."context" = 'workspace' AND "workspace_files"."workspace_id" IS NOT NULL) OR ("workspace_files"."context" = 'project' AND "workspace_files"."project_id" IS NOT NULL)); --> statement-breakpoint SET lock_timeout = '5s'; From 347cc6c5b0063e2dde0f53726f68a82f432dae0c Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 6 Oct 2026 13:57:18 -0700 Subject: [PATCH 031/113] fix(realtime): fence file joins by owner-qualified room --- .../handlers/file-doc-project.integration.ts | 92 +++++++++++++++++++ apps/realtime/src/handlers/file-doc.ts | 35 +++---- 2 files changed, 106 insertions(+), 21 deletions(-) diff --git a/apps/realtime/src/handlers/file-doc-project.integration.ts b/apps/realtime/src/handlers/file-doc-project.integration.ts index 690d6989cf5..6b5e3f8531a 100644 --- a/apps/realtime/src/handlers/file-doc-project.integration.ts +++ b/apps/realtime/src/handlers/file-doc-project.integration.ts @@ -13,6 +13,7 @@ import { type JoinFileDocError, type JoinFileDocPayload, type JoinFileDocSuccess, + type LeaveFileDocPayload, } from '@sim/realtime-protocol/file-doc' import { createDeferred } from '@sim/testing/helpers/deferred' import { getErrorMessage } from '@sim/utils/errors' @@ -296,6 +297,97 @@ describe('Project documents across the Socket.IO and internal HTTP boundary', () }) } + for (const action of ['join', 'leave'] as const) { + check( + action === 'join' + ? 'a join for another owner supersedes the pending admission for the same file' + : 'a leave for another owner preserves the pending admission for the same file', + async () => { + const actor = `owner-intent-${generateId()}` + actors.set(actor, 'read') + const gate = { + requests: 0, + entered: createDeferred(), + release: createDeferred(), + status: 200, + } + accessGates.set(actor, gate) + const socket = connect(socketUrl, { transports: ['websocket'], auth: { actor } }) + clients.push(socket) + await new Promise((resolve) => socket.once('connect', resolve)) + const server = io.sockets.sockets.get(socket.id ?? '') + if (!server) throw new Error('Pending socket missing') + const originalJoin = server.listeners(FILE_DOC_EVENTS.JOIN)[0] + const originalLeave = server.listeners(FILE_DOC_EVENTS.LEAVE)[0] + if (!originalJoin || !originalLeave) throw new Error('Document lifecycle handlers missing') + const provider = new Y.Doc() + const nextProvider = new Y.Doc() + const done = createDeferred() + const actionDone = createDeferred() + const observeJoin = async (payload: JoinFileDocPayload) => { + try { + await originalJoin.call(server, payload) + } finally { + if (payload.clientId === provider.clientID) done.resolve() + else actionDone.resolve() + } + } + const observeLeave = async (payload: LeaveFileDocPayload) => { + try { + await originalLeave.call(server, payload) + } finally { + actionDone.resolve() + } + } + server.off(FILE_DOC_EVENTS.JOIN, originalJoin) + server.on(FILE_DOC_EVENTS.JOIN, observeJoin) + server.off(FILE_DOC_EVENTS.LEAVE, originalLeave) + server.on(FILE_DOC_EVENTS.LEAVE, observeLeave) + const admissions: JoinFileDocSuccess[] = [] + const errors: JoinFileDocError[] = [] + socket.on(FILE_DOC_EVENTS.JOIN_SUCCESS, (payload) => admissions.push(payload)) + socket.on(FILE_DOC_EVENTS.JOIN_ERROR, (payload) => errors.push(payload)) + try { + socket.emit(FILE_DOC_EVENTS.JOIN, { + fileId, + projectId, + clientId: provider.clientID, + schemaVersion: FILE_DOC_SCHEMA_VERSION, + }) + await gate.entered.promise + const otherOwner = { fileId, projectId: generateId() } + if (action === 'join') + socket.emit(FILE_DOC_EVENTS.JOIN, { + ...otherOwner, + clientId: nextProvider.clientID, + schemaVersion: FILE_DOC_SCHEMA_VERSION, + }) + else socket.emit(FILE_DOC_EVENTS.LEAVE, otherOwner) + await actionDone.promise + gate.release.resolve() + await done.promise + const drained = new Promise((resolve) => socket.once('fixture-drained', resolve)) + server.emit('fixture-drained') + await drained + const shouldAdmit = action === 'leave' + expect(server.rooms.has(`project-file-doc:${projectId}/${fileId}`)).toBe(shouldAdmit) + expect(admissions).toHaveLength(shouldAdmit ? 1 : 0) + expect(errors.map((error) => error.code)).toEqual(shouldAdmit ? [] : ['NOT_FOUND']) + } finally { + gate.release.resolve() + accessGates.delete(actor) + server.off(FILE_DOC_EVENTS.JOIN, observeJoin) + server.on(FILE_DOC_EVENTS.JOIN, originalJoin) + server.off(FILE_DOC_EVENTS.LEAVE, observeLeave) + server.on(FILE_DOC_EVENTS.LEAVE, originalLeave) + socket.disconnect() + provider.destroy() + nextProvider.destroy() + } + } + ) + } + for (const rejectLeave of [false, true]) { check( `a stale rejoin preserves the admitted document when delayed cleanup ${rejectLeave ? 'rejects' : 'resolves'}`, diff --git a/apps/realtime/src/handlers/file-doc.ts b/apps/realtime/src/handlers/file-doc.ts index 36e37d22f73..418f7b81b2b 100644 --- a/apps/realtime/src/handlers/file-doc.ts +++ b/apps/realtime/src/handlers/file-doc.ts @@ -1515,11 +1515,8 @@ export function setupWorkspaceFileDocHandlers( roomManager: IRoomManager ) { const io = roomManager.io - // The file this socket currently intends to edit (set when a join starts). A leave targeting it - // — or an unscoped leave — advances the join generation to cancel an in-flight join, so a join - // awaiting authorization can't complete after the client left and register a ghost owner. A - // leave for a DIFFERENT file must NOT cancel it (a document switch), mirroring workspace-files. - let currentFileId: string | null = null + // Owner-qualified intent fences delayed admissions and leaves across document switches. + let currentFileRoom: string | null = null /** Co-mounted providers share invalidation membership until their last admission settles. */ const pendingMemberships = new Map() @@ -1594,19 +1591,18 @@ export function setupWorkspaceFileDocHandlers( return } - // A generation represents the socket's intended FILE, not an individual provider. Co-mounted - // providers for the same file must be allowed to join concurrently; switching files advances the - // generation so every in-flight join for the old file is cancelled together. - if (currentFileId !== fileId) { + const room = fileDocRoom(fileId, projectId) + const name = roomName(room) + + // Co-mounted providers share a generation only while their owner-qualified target matches. + if (currentFileRoom !== name) { generation = (joinGeneration.get(socket.id) ?? 0) + 1 joinGeneration.set(socket.id, generation) - currentFileId = fileId + currentFileRoom = name } else { generation = joinGeneration.get(socket.id) ?? 0 } - const room = fileDocRoom(fileId, projectId) - const name = roomName(room) const admissionName = fileDocAdmissionRoom(fileId, projectId) const authorizeJoin = () => @@ -1957,14 +1953,12 @@ export function setupWorkspaceFileDocHandlers( socket.on(FILE_DOC_EVENTS.LEAVE, async (payload?: LeaveFileDocPayload) => { try { - // Cancel an in-flight join whose file the client is now leaving (or an unscoped leave): a - // join still awaiting authorization would otherwise complete after the client left, register - // as an owner, and broadcast a ghost collaborator until disconnect. Guard on the current - // file intent so a stale/deferred leave for a DIFFERENT file can't abort the join the client - // has since switched to (bumping the generation blindly caused that regression in #5941). - if (!payload?.fileId || payload.fileId === currentFileId) { + const leavingRoom = payload?.fileId + ? roomName(fileDocRoom(payload.fileId, payload.projectId)) + : undefined + if (!leavingRoom || leavingRoom === currentFileRoom) { joinGeneration.set(socket.id, (joinGeneration.get(socket.id) ?? 0) + 1) - currentFileId = null + currentFileRoom = null } // Tear down membership only for a REGISTERED room; a leave that raced ahead of an in-flight // join (nothing registered yet) has already cancelled it above. @@ -1972,8 +1966,7 @@ export function setupWorkspaceFileDocHandlers( if (!name) return // Scope the leave to the named file when provided: a deferred leave from a // prior document must not evict the socket from one it has since opened. - if (payload?.fileId && roomName(fileDocRoom(payload.fileId, payload.projectId)) !== name) - return + if (leavingRoom && leavingRoom !== name) return cleanupFileDocForSocket(socket.id, io) await leaveUnclaimedRoom(name) } catch (error) { From 94125e1aab9ae9f765d9d62f2323a27ba847aa83 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 6 Oct 2026 17:44:25 -0700 Subject: [PATCH 032/113] fix(files): retain shared resources after creator deletion Keep durable workspace and Project files and file folders with a nullable live creator while preserving other creator cascades and native owner constraints. Require a creator at insertion and remove the original-creator snapshot schema and backfill. Accept missing creator attribution throughout workspace metadata, v2 serialization and UI, and authorize canonical shared file delivery against current membership. Preserve stored version authors independently. Exercise account departure/deletion, retained storage and history, rollback, and concurrent writes against real PostgreSQL. --- apps/docs/openapi-v2-files-audit.json | 60 +++- apps/sim/app/api/files/authorization.ts | 5 + .../app/api/files/uploads/finalizers.test.ts | 2 +- .../v2/files/[fileId]/content/route.test.ts | 4 +- .../v2/files/[fileId]/metadata/route.test.ts | 4 +- .../app/api/v2/files/[fileId]/route.test.ts | 4 +- .../versions/[version]/revert/route.test.ts | 3 +- apps/sim/app/api/v2/files/route.test.ts | 4 +- apps/sim/app/api/v2/files/utils.ts | 26 +- .../workspace/[workspaceId]/files/files.tsx | 16 +- .../sub-block/hooks/use-resource-folders.ts | 3 +- apps/sim/hooks/queries/utils/folder-tree.ts | 6 +- apps/sim/lib/api/contracts/v2/files.ts | 3 +- .../api/contracts/workspace-file-folders.ts | 2 +- apps/sim/lib/api/contracts/workspace-files.ts | 2 +- apps/sim/lib/internal/file/operations.ts | 8 +- .../file-round-trip-provenance.test.ts | 2 +- .../creator-lifetime.integration.ts | 232 +++++++++++++ .../workspace-file-folder-manager.ts | 14 +- .../workspace/workspace-file-manager.ts | 41 +-- .../workspace/workspace-file-versions.ts | 13 +- .../read-workspace-file-secret-provenance.ts | 2 +- .../db/file-creator-lifetime.integration.ts | 312 +++++++++--------- .../migrations/0400_file_creator_lifetime.sql | 78 ++--- .../db/migrations/meta/0400_snapshot.json | 16 +- .../db/migrations/meta/0401_snapshot.json | 16 +- .../db/migrations/meta/0402_snapshot.json | 16 +- .../db/migrations/meta/0403_snapshot.json | 16 +- packages/db/schema.ts | 12 +- .../0032_backfill_project_file_creators.ts | 49 --- packages/db/script-migrations/index.ts | 2 - .../db/scripts/reconcile-file-ownership.ts | 2 +- packages/sim-cli/src/generated/v2-api.ts | 24 +- .../src/mocks/schema-tables.generated.ts | 2 - 34 files changed, 553 insertions(+), 448 deletions(-) create mode 100644 apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts delete mode 100644 packages/db/script-migrations/0032_backfill_project_file_creators.ts diff --git a/apps/docs/openapi-v2-files-audit.json b/apps/docs/openapi-v2-files-audit.json index bc682bdbbe7..7b335cb5e4f 100644 --- a/apps/docs/openapi-v2-files-audit.json +++ b/apps/docs/openapi-v2-files-audit.json @@ -3948,10 +3948,17 @@ "maxLength": 4096 }, "uploadedByEmail": { - "type": "string", - "format": "email", - "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", - "description": "Current email address of the uploader.", + "anyOf": [ + { + "type": "string", + "format": "email", + "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" + }, + { + "type": "null" + } + ], + "description": "Current email address of the creator, or null after account deletion.", "examples": ["jane@example.com"] }, "uploadedAt": { @@ -4085,10 +4092,17 @@ "maxLength": 4096 }, "uploadedByEmail": { - "type": "string", - "format": "email", - "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", - "description": "Current email address of the uploader.", + "anyOf": [ + { + "type": "string", + "format": "email", + "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" + }, + { + "type": "null" + } + ], + "description": "Current email address of the creator, or null after account deletion.", "examples": ["jane@example.com"] }, "uploadedAt": { @@ -5421,10 +5435,17 @@ "maxLength": 4096 }, "uploadedByEmail": { - "type": "string", - "format": "email", - "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", - "description": "Current email address of the uploader.", + "anyOf": [ + { + "type": "string", + "format": "email", + "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" + }, + { + "type": "null" + } + ], + "description": "Current email address of the creator, or null after account deletion.", "examples": ["jane@example.com"] }, "uploadedAt": { @@ -6324,10 +6345,17 @@ "maxLength": 4096 }, "uploadedByEmail": { - "type": "string", - "format": "email", - "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", - "description": "Current email address of the uploader.", + "anyOf": [ + { + "type": "string", + "format": "email", + "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" + }, + { + "type": "null" + } + ], + "description": "Current email address of the creator, or null after account deletion.", "examples": ["jane@example.com"] }, "uploadedAt": { diff --git a/apps/sim/app/api/files/authorization.ts b/apps/sim/app/api/files/authorization.ts index a9ef1036560..213c8fdf168 100644 --- a/apps/sim/app/api/files/authorization.ts +++ b/apps/sim/app/api/files/authorization.ts @@ -130,6 +130,11 @@ export async function verifyFileAccess( try { const canonical = await getFileMetadataByKey(cloudKey, undefined, { includeDeleted: true }) if (canonical?.projectId != null || canonical?.context === 'project') return false + if (canonical && isWorkspaceScopedContext(canonical.context)) { + if (canonical.deletedAt || !canonical.workspaceId) return false + const permission = await getUserEntityPermissions(userId, 'workspace', canonical.workspaceId) + return workspacePermissionSatisfies(permission, requireWrite) + } const keyContext = inferContextFromKey(cloudKey) /** Organization logos are changed only through the organization-authorized upload lifecycle. */ if (keyContext === 'organization-logos') return !requireWrite diff --git a/apps/sim/app/api/files/uploads/finalizers.test.ts b/apps/sim/app/api/files/uploads/finalizers.test.ts index 5545692a4c8..748c59cdfef 100644 --- a/apps/sim/app/api/files/uploads/finalizers.test.ts +++ b/apps/sim/app/api/files/uploads/finalizers.test.ts @@ -36,7 +36,7 @@ import { } from '@/app/api/files/uploads/finalizers' import type { InternalUploadPurpose } from '@/app/api/files/uploads/purposes' -usersQueriesMockFns.mockGetUserEmailsByIds.mockImplementation( +usersQueriesMockFns.mockFindUserEmailsByIds.mockImplementation( async () => new Map([['user-1', 'ada@example.com']]) ) diff --git a/apps/sim/app/api/v2/files/[fileId]/content/route.test.ts b/apps/sim/app/api/v2/files/[fileId]/content/route.test.ts index bbdf598dfac..0b6b7cfb709 100644 --- a/apps/sim/app/api/v2/files/[fileId]/content/route.test.ts +++ b/apps/sim/app/api/v2/files/[fileId]/content/route.test.ts @@ -42,7 +42,7 @@ vi.mock('@/lib/users/queries', () => usersQueriesMock) import { NoWorkspaceAccessError } from '@/lib/core/application' import { PATCH, PUT } from '@/app/api/v2/files/[fileId]/content/route' -const { mockGetUserEmailsByIds } = usersQueriesMockFns +const { mockFindUserEmailsByIds } = usersQueriesMockFns const WORKSPACE_ID = 'workspace-1' const FILE_ID = 'wf_1' @@ -104,7 +104,7 @@ describe('PUT /api/v2/files/[fileId]/content', () => { mocks.admit.mockResolvedValue(undefined) mocks.editContent.mockResolvedValue({ file: record, lineCount: 1 }) mocks.updateContent.mockResolvedValue({ file: record }) - mockGetUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) + mockFindUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) }) it('performs authenticated admission before parsing a large or malformed body', async () => { diff --git a/apps/sim/app/api/v2/files/[fileId]/metadata/route.test.ts b/apps/sim/app/api/v2/files/[fileId]/metadata/route.test.ts index 1534cc41728..e7cc5b1b991 100644 --- a/apps/sim/app/api/v2/files/[fileId]/metadata/route.test.ts +++ b/apps/sim/app/api/v2/files/[fileId]/metadata/route.test.ts @@ -29,7 +29,7 @@ import { NoWorkspaceAccessError } from '@/lib/core/application' import { OrchestrationError } from '@/lib/core/orchestration/types' import { GET } from '@/app/api/v2/files/[fileId]/metadata/route' -const { mockGetUserEmailsByIds } = usersQueriesMockFns +const { mockFindUserEmailsByIds } = usersQueriesMockFns const WORKSPACE_ID = 'workspace-1' const FILE_ID = 'wf_1' @@ -93,7 +93,7 @@ describe('GET /api/v2/files/[fileId]/metadata', () => { file: { ...buildRecord(), currentVersion: 3 }, share: SHARE, }) - mockGetUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) + mockFindUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) }) it('conceals cross-workspace authorization as not found', async () => { diff --git a/apps/sim/app/api/v2/files/[fileId]/route.test.ts b/apps/sim/app/api/v2/files/[fileId]/route.test.ts index 99835d09c11..91318f24e8d 100644 --- a/apps/sim/app/api/v2/files/[fileId]/route.test.ts +++ b/apps/sim/app/api/v2/files/[fileId]/route.test.ts @@ -47,7 +47,7 @@ import { NoWorkspaceAccessError } from '@/lib/core/application' import { OrchestrationError } from '@/lib/core/orchestration/types' import { GET, PATCH } from '@/app/api/v2/files/[fileId]/route' -const { mockGetUserEmailsByIds } = usersQueriesMockFns +const { mockFindUserEmailsByIds } = usersQueriesMockFns const WORKSPACE_ID = 'workspace-1' const FILE_ID = 'wf_1' @@ -103,7 +103,7 @@ describe('v2 single-file routes', () => { workspaceId: WORKSPACE_ID, deleted: true, }) - mockGetUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) + mockFindUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) mocks.authorizeDownload.mockResolvedValue(undefined) }) diff --git a/apps/sim/app/api/v2/files/[fileId]/versions/[version]/revert/route.test.ts b/apps/sim/app/api/v2/files/[fileId]/versions/[version]/revert/route.test.ts index c6a62c7bcb1..59fec1a820d 100644 --- a/apps/sim/app/api/v2/files/[fileId]/versions/[version]/revert/route.test.ts +++ b/apps/sim/app/api/v2/files/[fileId]/versions/[version]/revert/route.test.ts @@ -28,7 +28,7 @@ vi.mock('@/lib/users/queries', () => usersQueriesMock) import { workspaceFileRevision } from '@/lib/workspace-files/application/file-revision' import { POST } from '@/app/api/v2/files/[fileId]/versions/[version]/revert/route' -const { mockGetUserEmailsByIds, mockFindUserEmailsByIds } = usersQueriesMockFns +const { mockFindUserEmailsByIds } = usersQueriesMockFns const WORKSPACE_ID = 'workspace-1' const FILE_ID = 'wf_1' @@ -93,7 +93,6 @@ describe('POST /api/v2/files/[fileId]/versions/[version]/revert', () => { reverted: true, revertedFrom: 3, }) - mockGetUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) mockFindUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) }) diff --git a/apps/sim/app/api/v2/files/route.test.ts b/apps/sim/app/api/v2/files/route.test.ts index e94b63a9cda..6587d0e58ab 100644 --- a/apps/sim/app/api/v2/files/route.test.ts +++ b/apps/sim/app/api/v2/files/route.test.ts @@ -33,7 +33,7 @@ vi.mock('@/lib/users/queries', () => usersQueriesMock) import { GET, POST } from '@/app/api/v2/files/route' -const { mockGetUserEmailsByIds } = usersQueriesMockFns +const { mockFindUserEmailsByIds } = usersQueriesMockFns const mockQueryFiles = workspaceFilesListMockFns.mockQueryWorkspaceFilePage @@ -81,7 +81,7 @@ describe('/api/v2/files', () => { nextKeys: undefined, }) mocks.createFile.mockResolvedValue({ file: FILE }) - mockGetUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) + mockFindUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) }) /** diff --git a/apps/sim/app/api/v2/files/utils.ts b/apps/sim/app/api/v2/files/utils.ts index b7956dccee2..7a3579b1f08 100644 --- a/apps/sim/app/api/v2/files/utils.ts +++ b/apps/sim/app/api/v2/files/utils.ts @@ -6,11 +6,7 @@ import { workspaceResourceWebUrl } from '@/lib/resources' import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace' import { workspaceFileVfsPath } from '@/lib/uploads/contexts/workspace/workspace-file-manager' import type { WorkspaceFileVersionRecord } from '@/lib/uploads/contexts/workspace/workspace-file-versions' -import { - findUserEmailsByIds, - getUserEmailsByIds, - requireResolvedUserEmail, -} from '@/lib/users/queries' +import { findUserEmailsByIds } from '@/lib/users/queries' import type { ReadWorkspaceFileTextResult } from '@/lib/workspace-files/application/read-workspace-file-text' import { parseWorkspaceFileFolderDisplayPath } from '@/lib/workspace-files/folder-display-path' @@ -22,7 +18,7 @@ import { parseWorkspaceFileFolderDisplayPath } from '@/lib/workspace-files/folde */ function serializeV2File( record: WorkspaceFileRecord, - uploadedByEmail: string, + uploadedByEmail: string | null, baseUrl: string ): V2File { const folderPath = record.folderId @@ -51,20 +47,22 @@ function serializeV2File( /** Resolves and serializes one public file attribution. */ export async function toV2File(record: WorkspaceFileRecord): Promise { - const emailByUserId = await getUserEmailsByIds([record.uploadedBy]) - return serializeV2File( - record, - requireResolvedUserEmail(emailByUserId, record.uploadedBy), - getBaseUrl() - ) + const [file] = await toV2Files([record]) + return file } /** Resolves a file page's attribution in one query before serialization. */ export async function toV2Files(records: WorkspaceFileRecord[]): Promise { - const emailByUserId = await getUserEmailsByIds(records.map((record) => record.uploadedBy)) + const emailByUserId = await findUserEmailsByIds( + records.flatMap((record) => (record.uploadedBy ? [record.uploadedBy] : [])) + ) const baseUrl = getBaseUrl() return records.map((record) => - serializeV2File(record, requireResolvedUserEmail(emailByUserId, record.uploadedBy), baseUrl) + serializeV2File( + record, + record.uploadedBy ? (emailByUserId.get(record.uploadedBy) ?? null) : null, + baseUrl + ) ) } diff --git a/apps/sim/app/workspace/[workspaceId]/files/files.tsx b/apps/sim/app/workspace/[workspaceId]/files/files.tsx index 9cb16f9a489..56430974ffa 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/files.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/files.tsx @@ -687,7 +687,9 @@ function FilesContent() { } if (uploadedByFilter.length > 0) { - result = result.filter((f) => uploadedByFilter.includes(f.uploadedBy)) + result = result.filter( + (f) => f.uploadedBy !== null && uploadedByFilter.includes(f.uploadedBy) + ) } return result @@ -720,7 +722,7 @@ function FilesContent() { : sortColumn === 'updated' ? new Date(folder.updatedAt).getTime() : sortColumn === 'owner' - ? (membersById.get(folder.userId)?.name ?? null) + ? (membersById.get(folder.userId ?? '')?.name ?? null) : folder.name, }) } @@ -740,7 +742,7 @@ function FilesContent() { : sortColumn === 'updated' ? new Date(file.updatedAt).getTime() : sortColumn === 'owner' - ? (membersById.get(file.uploadedBy)?.name ?? null) + ? (membersById.get(file.uploadedBy ?? '')?.name ?? null) : file.name, }) } @@ -782,7 +784,9 @@ function FilesContent() { label: FOLDER_TYPE_LABEL, }, created: timeCell(folder.createdAt), - owner: ownerCell(folder.userId, membersById), + owner: folder.userId + ? ownerCell(folder.userId, membersById) + : { label: 'Deleted user' }, updated: timeCell(folder.updatedAt), /** * A folder's location is its parent's path, not its own. Built only while @@ -816,7 +820,9 @@ function FilesContent() { label: formatFileType(file.type, file.name), }, created: timeCell(file.uploadedAt), - owner: ownerCell(file.uploadedBy, membersById), + owner: file.uploadedBy + ? ownerCell(file.uploadedBy, membersById) + : { label: 'Deleted user' }, updated: timeCell(file.updatedAt), location: isSearching ? { label: folderLocationLabel(file.folderId, folderById, FILES_HEADER.rootLabel) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-resource-folders.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-resource-folders.ts index f7ab9a2dcb7..a9d0838562f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-resource-folders.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-resource-folders.ts @@ -84,13 +84,12 @@ export function useResourceFolders( WORKSPACE_FILE_BROWSER_INVALIDATION_KEY ) - const rows = useMemo(() => { + const rows = useMemo[]>(() => { if (isFileResource) { return (files.data ?? []).map((folder) => ({ id: folder.id, resourceType: 'file' as const, name: folder.name, - userId: folder.userId, workspaceId: folder.workspaceId, parentId: folder.parentId, locked: false, diff --git a/apps/sim/hooks/queries/utils/folder-tree.ts b/apps/sim/hooks/queries/utils/folder-tree.ts index 65144d7f45e..f32e491e4cc 100644 --- a/apps/sim/hooks/queries/utils/folder-tree.ts +++ b/apps/sim/hooks/queries/utils/folder-tree.ts @@ -49,7 +49,9 @@ export function getFolderPath( /** Returns the canonical public API path for a folder and rejects corrupt trees. */ export function getCanonicalFolderPath( folderId: string | null | undefined, - folders: Record | Map + folders: + | Record> + | Map> ): string { if (!folderId) return '/' @@ -61,7 +63,7 @@ export function getCanonicalFolderPath( if (visited.has(currentFolderId)) throw new Error('Folder tree contains a cycle') visited.add(currentFolderId) - const folder: WorkflowFolder | undefined = + const folder: Pick | undefined = folders instanceof Map ? folders.get(currentFolderId) : folders[currentFolderId] if (!folder) throw new Error(`Folder ${currentFolderId} was not found`) segments.unshift(folder.name) diff --git a/apps/sim/lib/api/contracts/v2/files.ts b/apps/sim/lib/api/contracts/v2/files.ts index 9e8a8003ca5..1a91cd87d20 100644 --- a/apps/sim/lib/api/contracts/v2/files.ts +++ b/apps/sim/lib/api/contracts/v2/files.ts @@ -123,7 +123,8 @@ export const v2FileSchema = z ), uploadedByEmail: z .email({ pattern: z.regexes.html5Email }) - .describe('Current email address of the uploader.') + .nullable() + .describe('Current email address of the creator, or null after account deletion.') .meta({ examples: ['jane@example.com'] }), /** ISO-8601 timestamp. */ uploadedAt: z diff --git a/apps/sim/lib/api/contracts/workspace-file-folders.ts b/apps/sim/lib/api/contracts/workspace-file-folders.ts index 66620f36363..082e422c34a 100644 --- a/apps/sim/lib/api/contracts/workspace-file-folders.ts +++ b/apps/sim/lib/api/contracts/workspace-file-folders.ts @@ -19,7 +19,7 @@ export const listWorkspaceFileFoldersQuerySchema = z.object({ export const workspaceFileFolderSchema = z.object({ id: z.string(), workspaceId: z.string(), - userId: z.string(), + userId: z.string().nullable(), name: z.string(), parentId: z.string().nullable(), path: z.string(), diff --git a/apps/sim/lib/api/contracts/workspace-files.ts b/apps/sim/lib/api/contracts/workspace-files.ts index 22453430e7e..ab13275bb43 100644 --- a/apps/sim/lib/api/contracts/workspace-files.ts +++ b/apps/sim/lib/api/contracts/workspace-files.ts @@ -152,7 +152,7 @@ export const workspaceFileRecordSchema = z.object({ /** Intrinsic image dimensions (px), populated lazily; null for non-images/un-backfilled rows. */ width: z.number().int().positive().nullable().optional(), height: z.number().int().positive().nullable().optional(), - uploadedBy: z.string(), + uploadedBy: z.string().nullable(), folderId: z.string().nullable(), folderPath: z.string().nullable().optional(), deletedAt: z.coerce.date().nullable().optional(), diff --git a/apps/sim/lib/internal/file/operations.ts b/apps/sim/lib/internal/file/operations.ts index eebc28d1c07..e0f40ea3ea9 100644 --- a/apps/sim/lib/internal/file/operations.ts +++ b/apps/sim/lib/internal/file/operations.ts @@ -515,7 +515,7 @@ async function bindSelectedContentFile( context: 'workspace', contentUpdatedAt: metadata.contentUpdatedAt ?? undefined, }, - ownerUserId: metadata.uploadedBy, + ownerUserId: metadata.uploadedBy ?? undefined, } } @@ -1195,7 +1195,7 @@ export async function executeFileManageOperation( context: 'workspace', contentUpdatedAt: file.contentUpdatedAt ?? undefined, }, - ownerUserId: file.uploadedBy, + ownerUserId: file.uploadedBy ?? undefined, }, ] }) @@ -1890,7 +1890,7 @@ export async function executeFileManageOperation( context: 'workspace', contentUpdatedAt: file.contentUpdatedAt ?? undefined, }, - ownerUserId: file.uploadedBy, + ownerUserId: file.uploadedBy ?? undefined, }, ] }) @@ -2080,7 +2080,7 @@ export async function executeFileManageOperation( context: 'workspace', contentUpdatedAt: file.contentUpdatedAt ?? undefined, }, - ownerUserId: file.uploadedBy, + ownerUserId: file.uploadedBy ?? undefined, }, ] }) diff --git a/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts b/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts index bb248c8b12b..356c0940d72 100644 --- a/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts +++ b/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts @@ -107,7 +107,7 @@ workspaceFileFoldersMockFns.mockBuildWorkspaceFileFolderPathMap.mockImplementati workspaceFileFoldersMockFns.mockNormalizeWorkspaceFileItemName.mockImplementation( (name: string) => name ) -usersQueriesMockFns.mockGetUserEmailsByIds.mockImplementation( +usersQueriesMockFns.mockFindUserEmailsByIds.mockImplementation( async () => new Map([['reader', 'reader@example.test']]) ) usersQueriesMockFns.mockRequireResolvedUserEmail.mockImplementation( diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts new file mode 100644 index 00000000000..6d4db312fdf --- /dev/null +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts @@ -0,0 +1,232 @@ +import { mkdtempSync } from 'node:fs' +import { mkdir, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { dirname, join } from 'node:path' +import { db } from '@sim/db' +import { + folder, + permissions, + projectWorkspace, + user, + userStats, + workspace, + workspaceFiles, +} from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' +import { createSessionPrincipal } from '@sim/testing/factories/principal.factory' +import { setUploadDirServer, uploadsSetupMock } from '@sim/testing/mocks/uploads-setup.mock' +import { getErrorMessage } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { eq, inArray } from 'drizzle-orm' +import { afterAll, expect, it, vi } from 'vitest' + +vi.mock('@/lib/uploads/core/setup.server', () => uploadsSetupMock) + +import { resolveStorageBillingContext } from '@/lib/billing/storage/context' +import { + createWorkspaceFileFolder, + listFileFolders, + listWorkspaceFileFolders, +} from '@/lib/uploads/contexts/workspace/workspace-file-folder-manager' +import { + fetchWorkspaceFileBuffer, + getWorkspaceFile, + updateWorkspaceFileContent, + uploadWorkspaceFile, +} from '@/lib/uploads/contexts/workspace/workspace-file-manager' +import { queryWorkspaceFileVersions } from '@/lib/uploads/contexts/workspace/workspace-file-versions' +import { deleteUserAccount } from '@/lib/users/account-deletion' +import { readWorkspaceFileMetadata } from '@/lib/workspace-files/application/read-workspace-file-metadata' +import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access' +import { verifyFileAccess } from '@/app/api/files/authorization' +import { toV2File } from '@/app/api/v2/files/utils' + +const storageRoot = mkdtempSync(join(tmpdir(), 'sim-creator-lifetime-')) +setUploadDirServer(storageRoot) +const creatorId = generateId() +const collaboratorId = generateId() +const workspaceId = generateId() +const checks: { name: string; status: string; durationMs: number; error?: string }[] = [] + +afterAll(async () => { + try { + await db + .delete(workspaceFiles) + .where(inArray(workspaceFiles.userId, [creatorId, collaboratorId])) + const [binding] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, workspaceId)) + if (binding) { + await db.delete(workspaceFiles).where(eq(workspaceFiles.projectId, binding.projectId)) + await db.delete(folder).where(eq(folder.projectId, binding.projectId)) + } + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) + await db.delete(user).where(inArray(user.id, [creatorId, collaboratorId])) + } finally { + await db.$client.end() + await rm(storageRoot, { recursive: true, force: true }) + const reportPath = process.env.FILE_CREATOR_APPLICATION_REPORT_PATH + if (reportPath) { + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) + } + } +}) + +it('departure revokes access without erasing attribution; account deletion retains shared bytes, folders and history', async () => { + const started = performance.now() + const name = 'real departure, account deletion, current collaborator reads and writes' + try { + await db.insert(user).values( + [creatorId, collaboratorId].map((id) => ({ + id, + email: `${id}@creator.invalid`, + name: 'Creator lifetime fixture', + emailVerified: true, + createdAt: new Date(), + updatedAt: new Date(), + })) + ) + await db + .insert(userStats) + .values([creatorId, collaboratorId].map((userId) => ({ id: generateId(), userId }))) + await insertWorkspaceFixture(db, { + id: workspaceId, + ownerId: collaboratorId, + billedAccountUserId: collaboratorId, + name: 'Shared files', + }) + await db.insert(permissions).values( + [creatorId, collaboratorId].map((id) => ({ + id: generateId(), + userId: id, + entityType: 'workspace' as const, + entityId: workspaceId, + permissionType: 'admin' as const, + })) + ) + const createdFolder = await createWorkspaceFileFolder({ + workspaceId, + userId: creatorId, + name: 'Docs', + }) + const uploaded = await uploadWorkspaceFile( + workspaceId, + creatorId, + Buffer.from('original'), + 'notes.txt', + 'text/plain', + { folderId: createdFolder.id, notifyWorkspaceChange: false } + ) + await updateWorkspaceFileContent( + workspaceId, + uploaded.id, + creatorId, + Buffer.from('current'), + undefined, + { version: { source: 'api', authorUserId: creatorId }, syncLiveDoc: false } + ) + const [binding] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, workspaceId)) + const projectFolderId = generateId() + await db.insert(folder).values({ + id: projectFolderId, + projectId: binding.projectId, + resourceType: 'file', + userId: creatorId, + name: 'Project docs', + }) + const [before] = await db + .select() + .from(workspaceFiles) + .where(eq(workspaceFiles.id, uploaded.id)) + const projectFileId = generateId() + await db.insert(workspaceFiles).values({ + ...before, + id: projectFileId, + key: `project/${binding.projectId}/notes.txt`, + workspaceId: null, + projectId: binding.projectId, + context: 'project', + folderId: projectFolderId, + }) + const billingBefore = await resolveStorageBillingContext(workspaceId) + const creator = createSessionPrincipal({ userId: creatorId }) + const collaborator = createSessionPrincipal({ userId: collaboratorId }) + await expect( + readWorkspaceFileMetadata.execute({ + principal: creator, + input: { fileId: uploaded.id, assertedWorkspaceId: workspaceId }, + }) + ).resolves.toBeTruthy() + expect(await verifyFileAccess(before.key, creatorId, undefined, 'general')).toBe(true) + const revoked = await db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { workspaceId, userId: creatorId }) + ) + expect(revoked.revoked).toBe(true) + expect((await getWorkspaceFile(workspaceId, uploaded.id))?.uploadedBy).toBe(creatorId) + await expect( + readWorkspaceFileMetadata.execute({ + principal: creator, + input: { fileId: uploaded.id, assertedWorkspaceId: workspaceId }, + }) + ).rejects.toThrow() + for (const context of ['workspace', 'general', 'copilot', 'profile-pictures'] as const) { + expect(await verifyFileAccess(before.key, creatorId, undefined, context)).toBe(false) + expect(await verifyFileAccess(before.key, collaboratorId, undefined, context)).toBe(true) + } + const deletion = await deleteUserAccount(creatorId) + expect(deletion.blockers).toEqual([]) + expect(await db.select({ id: user.id }).from(user).where(eq(user.id, creatorId))).toEqual([]) + const retained = await getWorkspaceFile(workspaceId, uploaded.id, { throwOnError: true }) + if (!retained) throw new Error('Shared file was deleted with its creator') + expect(retained.uploadedBy).toBeNull() + expect((await fetchWorkspaceFileBuffer(retained, { maxBytes: 1024 })).toString()).toBe( + 'current' + ) + expect( + (await listWorkspaceFileFolders(workspaceId)).find((folder) => folder.id === createdFolder.id) + ?.userId + ).toBeNull() + expect( + (await listFileFolders({ entityType: 'project', entityId: binding.projectId }))[0].userId + ).toBeNull() + expect( + (await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, projectFileId)))[0].userId + ).toBeNull() + expect((await toV2File(retained)).uploadedByEmail).toBeNull() + await expect( + readWorkspaceFileMetadata.execute({ + principal: collaborator, + input: { fileId: uploaded.id, assertedWorkspaceId: workspaceId }, + }) + ).resolves.toBeTruthy() + const history = await queryWorkspaceFileVersions(retained, { sortOrder: 'asc', limit: 10 }) + expect(history.versions).toHaveLength(2) + expect(history.versions.every((version) => version.authorUserIds.includes(creatorId))).toBe( + true + ) + await updateWorkspaceFileContent( + workspaceId, + uploaded.id, + collaboratorId, + Buffer.from('edited after deletion'), + undefined, + { version: { source: 'api', authorUserId: collaboratorId }, syncLiveDoc: false } + ) + expect((await getWorkspaceFile(workspaceId, uploaded.id))?.uploadedBy).toBeNull() + expect(await resolveStorageBillingContext(workspaceId)).toEqual(billingBefore) + checks.push({ name, status: 'passed', durationMs: performance.now() - started }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - started, + error: getErrorMessage(error), + }) + throw error + } +}) diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts index 88bc49dffb8..61d3f63ccf1 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts @@ -91,7 +91,7 @@ export class WorkspaceFileItemsNotFoundError extends OrchestrationError { export interface WorkspaceFileFolderRecord { id: string workspaceId: string - userId: string + userId: string | null name: string parentId: string | null path: string @@ -103,11 +103,9 @@ export interface WorkspaceFileFolderRecord { type FileFolderRecord = Omit< WorkspaceFileFolderRecord, - 'workspaceId' | 'userId' + 'workspaceId' > & { owner: O - userId: string | null - originalCreatorUserId: string | null } interface FileFolderListOptions { @@ -150,7 +148,6 @@ interface RawWorkspaceFileFolder { id: string workspaceId: string userId: string | null - originalCreatorUserId?: string | null name: string parentId: string | null sortOrder: number @@ -324,10 +321,10 @@ function mapFolder( } function workspaceFolderRecord(folder: FileFolderRecord): WorkspaceFileFolderRecord { - if (folder.owner.entityType !== 'workspace' || !folder.userId) + if (folder.owner.entityType !== 'workspace') throw new OrchestrationError('not_found', 'Folder not found') - const { owner, originalCreatorUserId, ...record } = folder - return { ...record, userId: folder.userId, workspaceId: owner.entityId } + const { owner, ...record } = folder + return { ...record, workspaceId: owner.entityId } } function mapFileFolder( @@ -339,7 +336,6 @@ function mapFileFolder( id: folder.id, owner, userId: folder.userId, - originalCreatorUserId: folder.originalCreatorUserId ?? null, name: folder.name, parentId: folder.parentId, path: paths.get(folder.id) ?? folder.name, 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 f9e5a6c017f..0088581f76d 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -160,7 +160,7 @@ export interface WorkspaceFileRecord { /** Intrinsic image pixel dimensions, populated lazily on first view. Null/absent for non-images. */ width?: number | null height?: number | null - uploadedBy: string + uploadedBy: string | null folderId?: string | null folderPath?: string | null deletedAt?: Date | null @@ -191,12 +191,7 @@ export interface VersionedWorkspaceFileRecord extends WorkspaceFileRecord { /** Shared file metadata never substitutes an environment ID for its owning Project. */ interface OwnedFileRecord - extends Omit< - WorkspaceFileRecord, - 'workspaceId' | 'storageContext' | 'vfsNamespace' | 'uploadedBy' - > { - uploadedBy: string | null - originalCreatorUserId: string | null + extends Omit { owner: O folderId: string | null folderPath: string | null @@ -1265,13 +1260,10 @@ function mapWorkspaceFileRecord( workspaceId: string, folderPaths: Map ): WorkspaceFileRecord { - if (!file.userId) throw new Error('Workspace file is missing its creator') return { ...omit(mapFileRecord(file, { entityType: 'workspace', entityId: workspaceId }, folderPaths), [ 'owner', - 'originalCreatorUserId', ]), - uploadedBy: file.userId, workspaceId: file.workspaceId || workspaceId, } } @@ -1297,7 +1289,6 @@ function mapFileRecord( width: file.width, height: file.height, uploadedBy: file.userId, - originalCreatorUserId: file.originalCreatorUserId, folderId: file.folderId, folderPath: file.folderId ? (folderPaths.get(file.folderId) ?? null) : null, deletedAt: file.deletedAt, @@ -1470,7 +1461,6 @@ const workspaceFileListColumns = { id: workspaceFiles.id, key: workspaceFiles.key, userId: workspaceFiles.userId, - originalCreatorUserId: workspaceFiles.originalCreatorUserId, workspaceId: workspaceFiles.workspaceId, folderId: workspaceFiles.folderId, originalName: workspaceFiles.originalName, @@ -1616,10 +1606,8 @@ export async function queryWorkspaceFiles( const result = await queryFileRecords({ entityType: 'workspace', entityId: workspaceId }, options) return { files: result.files.map((file) => { - if (!file.uploadedBy) throw new Error('Workspace file is missing its creator') return { - ...omit(file, ['owner', 'originalCreatorUserId']), - uploadedBy: file.uploadedBy, + ...omit(file, ['owner']), workspaceId, } }), @@ -2427,28 +2415,19 @@ export async function updateWorkspaceFileContent( } } - const pathPrefix = getServePathPrefix() const currentFolderPath = finalized.file.folderId === fileRecord.folderId ? fileRecord.folderPath : null logger.info(`Successfully updated workspace file content: ${finalized.file.originalName}`) - if (!finalized.file.userId) throw new Error('Workspace file is missing its creator') return { - id: finalized.file.id, - workspaceId: finalized.file.workspaceId || workspaceId, - name: finalized.file.originalName, - key: finalized.file.key, - path: `${pathPrefix}${encodeURIComponent(finalized.file.key)}?context=workspace`, - size: getWorkspaceFileSize(finalized.file), - type: finalized.file.contentType, - uploadedBy: finalized.file.userId, - folderId: finalized.file.folderId, - folderPath: currentFolderPath, - deletedAt: finalized.file.deletedAt, - uploadedAt: finalized.file.uploadedAt, - updatedAt: finalized.file.updatedAt, - contentUpdatedAt: finalized.file.contentUpdatedAt, + ...mapWorkspaceFileRecord( + finalized.file, + workspaceId, + finalized.file.folderId && currentFolderPath + ? new Map([[finalized.file.folderId, currentFolderPath]]) + : new Map() + ), currentVersion: finalized.currentVersion, } } catch (error) { diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts index bfe2a492e2f..821d4672110 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts @@ -227,12 +227,7 @@ export async function recordWorkspaceFileVersionInTx( ...contentColumns(previous, params.previousProvenance), contentHash: null, source: original ? 'upload' : 'unknown', - authorUserIds: - original && (previous.originalCreatorUserId ?? previous.userId) - ? [previous.originalCreatorUserId ?? previous.userId].filter( - (id): id is string => id !== null - ) - : [], + authorUserIds: original && previous.userId ? [previous.userId] : [], createdAt: previous.contentUpdatedAt, updatedAt: previous.contentUpdatedAt, }) @@ -418,7 +413,7 @@ async function pruneExcessWorkspaceFileVersionsInTx( type WorkspaceFileVersionSubject = Pick< WorkspaceFileRecord, 'id' | 'key' | 'size' | 'type' | 'uploadedAt' | 'updatedAt' | 'contentUpdatedAt' -> & { uploadedBy: string | null; originalCreatorUserId?: string | null } +> & { uploadedBy: string | null } /** One version of a workspace file as readers see it. */ export interface WorkspaceFileVersionRecord { @@ -489,7 +484,7 @@ function implicitCurrentVersion( const contentUpdatedAt = contentVersionTime(file) const original = !head && isOriginalUploadContent({ uploadedAt: file.uploadedAt, contentUpdatedAt }) - const originalAuthor = file.originalCreatorUserId ?? file.uploadedBy + const originalAuthor = file.uploadedBy return { fileId: file.id, version: (head?.version ?? 0) + 1, @@ -523,7 +518,6 @@ function withVersionSnapshot( sizeBytes: workspaceFiles.sizeBytes, contentType: workspaceFiles.contentType, userId: workspaceFiles.userId, - originalCreatorUserId: workspaceFiles.originalCreatorUserId, uploadedAt: workspaceFiles.uploadedAt, updatedAt: workspaceFiles.updatedAt, contentUpdatedAt: workspaceFiles.contentUpdatedAt, @@ -539,7 +533,6 @@ function withVersionSnapshot( size: getWorkspaceFileSize(row), type: row.contentType, uploadedBy: row.userId, - originalCreatorUserId: row.originalCreatorUserId, uploadedAt: row.uploadedAt, updatedAt: row.updatedAt, contentUpdatedAt: row.contentUpdatedAt, 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..22e9dbe4ee2 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 @@ -21,7 +21,7 @@ export const readWorkspaceFileSecretProvenance = defineAuthorizedWorkspaceFileUs resolveActiveWorkspaceFileContext(input), async execute({ input, context }): Promise<{ provenance: WorkspaceFileSecretProvenance - ownerUserId: string + ownerUserId: string | null }> { const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true }) if (!file) throw new OrchestrationError('not_found', 'File not found') diff --git a/packages/db/file-creator-lifetime.integration.ts b/packages/db/file-creator-lifetime.integration.ts index 23b09511a64..afb66681ecd 100644 --- a/packages/db/file-creator-lifetime.integration.ts +++ b/packages/db/file-creator-lifetime.integration.ts @@ -35,7 +35,7 @@ function check(name: string, run: () => Promise) { }) } -describe('Project creator lifetime in PostgreSQL', () => { +describe('Shared file creator lifetime in PostgreSQL', () => { const schemaName = `file_creator_${generateId().replaceAll('-', '')}` let sql: Sql let admin: Sql @@ -127,7 +127,7 @@ describe('Project creator lifetime in PostgreSQL', () => { }) check( - 'creator deletion preserves Project folders, heads, history and immutable attribution', + 'creator deletion preserves Project folders, heads and history with a nullable creator', async () => { await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) VALUES ('folder', 'Docs', 'user-a', 'file', 'project-a')` @@ -138,11 +138,11 @@ describe('Project creator lifetime in PostgreSQL', () => { const [before] = await sql`SELECT key, content_updated_at, secret_provenance_version FROM workspace_files` await sql`DELETE FROM "user" WHERE id = 'user-a'` - expect( - await sql`SELECT user_id, original_creator_user_id, project_id FROM workspace_files` - ).toEqual([{ user_id: null, original_creator_user_id: 'user-a', project_id: 'project-a' }]) - expect(await sql`SELECT user_id, original_creator_user_id, project_id FROM folder`).toEqual([ - { user_id: null, original_creator_user_id: 'user-a', project_id: 'project-a' }, + expect(await sql`SELECT user_id, project_id FROM workspace_files`).toEqual([ + { user_id: null, project_id: 'project-a' }, + ]) + expect(await sql`SELECT user_id, project_id FROM folder`).toEqual([ + { user_id: null, project_id: 'project-a' }, ]) expect( ( @@ -155,97 +155,68 @@ describe('Project creator lifetime in PostgreSQL', () => { } ) - check( - 'legacy workspace and personal creator cascades still remove only their own rows', - async () => { - await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + check('workspace creator deletion preserves durable files, folders and history', async () => { + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) VALUES ('folder', 'Docs', 'user-a', 'workspace-a', 'file')` - await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, folder_id) - VALUES ('file', 'workspace', 'user-a', 'workspace-a', 'folder'), - ('personal', 'copilot', 'user-a', NULL, NULL)` - await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` - await sql`DELETE FROM "user" WHERE id = 'user-a'` - expect(await sql`SELECT id FROM workspace_files`).toHaveLength(0) - expect(await sql`SELECT id FROM folder`).toHaveLength(0) - expect(await sql`SELECT id FROM workspace_file_version`).toHaveLength(0) - expect(await sql`SELECT id FROM workspace`).toHaveLength(3) + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, folder_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a', 'folder')` + await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` + await sql`DELETE FROM "user" WHERE id = 'user-a'` + expect(await sql`SELECT id, user_id FROM workspace_files`).toEqual([ + { id: 'file', user_id: null }, + ]) + expect(await sql`SELECT id, user_id FROM folder`).toEqual([{ id: 'folder', user_id: null }]) + expect(await sql`SELECT id FROM workspace_file_version`).toEqual([{ id: 'version' }]) + }) + + check('other file contexts and folder types retain creator cascades', async () => { + for (const context of [ + 'mothership', + 'chat', + 'execution', + 'knowledge-base', + 'workspace-logos', + 'copilot', + 'profile-pictures', + 'general', + ]) { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES (${context}, ${context}, 'user-a', ${['copilot', 'profile-pictures', 'general'].includes(context) ? null : 'workspace-a'})` } - ) + await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + VALUES ('workflow', 'Workflows', 'user-a', 'workspace-a', 'workflow')` + await sql`DELETE FROM "user" WHERE id = 'user-a'` + expect(await sql`SELECT id FROM workspace_files`).toEqual([]) + expect(await sql`SELECT id FROM folder`).toEqual([]) + }) - check( - 'backfills legacy Project attribution in bounded pages without changing live creator or content', - async () => { - await sql`ALTER TABLE workspace_files DISABLE TRIGGER workspace_files_creator_lifetime` - await sql`ALTER TABLE folder DISABLE TRIGGER folder_creator_lifetime` - try { - await sql`INSERT INTO workspace_files (id, context, user_id, project_id, original_name) - SELECT 'file-' || lpad(id::text, 4, '0'), 'project', 'user-a', 'project-a', 'document-' || id - FROM generate_series(1, 507) id` - await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) - SELECT 'folder-' || lpad(id::text, 4, '0'), 'Docs ' || id, 'user-a', 'file', 'project-a' - FROM generate_series(1, 507) id` - } finally { - await sql`ALTER TABLE workspace_files ENABLE TRIGGER workspace_files_creator_lifetime` - await sql`ALTER TABLE folder ENABLE TRIGGER folder_creator_lifetime` + check('new resources require a live creator in either scope', async () => { + for (const context of ['workspace', 'project']) { + for (const userId of [null, 'missing-user']) { + await expect(sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, project_id) + VALUES ('missing', ${context}, ${userId}, ${context === 'workspace' ? 'workspace-a' : null}, ${context === 'project' ? 'project-a' : null})`).rejects.toMatchObject( + { code: userId === null ? '23514' : '23503' } + ) + await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, workspace_id, project_id) + VALUES ('missing', 'Docs', ${userId}, 'file', ${context === 'workspace' ? 'workspace-a' : null}, ${context === 'project' ? 'project-a' : null})`).rejects.toMatchObject( + { code: userId === null ? '23514' : '23503' } + ) } - const { backfillProjectFileCreators } = await import( - '@sim/db/script-migrations/0032_backfill_project_file_creators' - ) - expect(await backfillProjectFileCreators(sql)).toEqual({ files: 507, folders: 507 }) - expect(await backfillProjectFileCreators(sql)).toEqual({ files: 0, folders: 0 }) - expect( - await sql`SELECT DISTINCT user_id, original_creator_user_id, key, secret_provenance_version FROM workspace_files` - ).toEqual([ - { - user_id: 'user-a', - original_creator_user_id: 'user-a', - key: 'unchanged-object-key', - secret_provenance_version: 1, - }, - ]) - await sql`DELETE FROM "user" WHERE id = 'user-a'` - expect( - ( - await sql`SELECT count(*)::integer AS count FROM workspace_files WHERE user_id IS NULL AND original_creator_user_id = 'user-a'` - )[0].count - ).toBe(507) - expect( - ( - await sql`SELECT count(*)::integer AS count FROM folder WHERE user_id IS NULL AND original_creator_user_id = 'user-a'` - )[0].count - ).toBe(507) } - ) + }) check( - 'rejects missing and forged Project creators and immutable snapshot replacement', + 'surviving workspace content remains editable without claiming creator attribution', async () => { - await expect(sql`INSERT INTO workspace_files (id, context, user_id, project_id, original_creator_user_id) - VALUES ('missing', 'project', NULL, 'project-a', 'user-a')`).rejects.toMatchObject({ - code: '23514', - }) - await expect(sql`INSERT INTO workspace_files (id, context, user_id, project_id, original_creator_user_id) - VALUES ('forged', 'project', 'user-a', 'project-a', 'user-b')`).rejects.toMatchObject({ - code: '23514', - }) - await sql`INSERT INTO workspace_files (id, context, user_id, project_id) - VALUES ('file', 'project', 'user-a', 'project-a')` - for (const creator of ['user-b', null]) { - await expect( - sql`UPDATE workspace_files SET original_creator_user_id = ${creator} WHERE id = 'file'` - ).rejects.toMatchObject({ code: '23514' }) - } - await expect( - sql`UPDATE workspace_files SET user_id = 'user-b' WHERE id = 'file'` - ).rejects.toMatchObject({ code: '23514' }) - await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, project_id, original_creator_user_id) - VALUES ('forged', 'Docs', 'user-a', 'file', 'project-a', 'user-b')`).rejects.toMatchObject({ - code: '23514', - }) - await expect(sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) - VALUES ('legacy-null', 'workspace', NULL, 'workspace-a')`).rejects.toMatchObject({ - code: '23514', - }) + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('file', 'workspace', 'user-a', 'workspace-a')` + await sql`DELETE FROM "user" WHERE id = 'user-a'` + await sql`UPDATE workspace_files SET key = 'edited-content' WHERE id = 'file'` + expect(await sql`SELECT user_id, key FROM workspace_files`).toEqual([ + { user_id: null, key: 'edited-content' }, + ]) + await sql`DELETE FROM workspace WHERE id = 'workspace-a'` + expect(await sql`SELECT id FROM workspace_files`).toEqual([]) } ) @@ -258,92 +229,119 @@ describe('Project creator lifetime in PostgreSQL', () => { await expect(sql`DELETE FROM "user" WHERE id = 'user-a'`).rejects.toMatchObject({ code: '23503', }) - expect((await sql`SELECT user_id, original_creator_user_id FROM workspace_files`)[0]).toEqual( - { user_id: 'user-a', original_creator_user_id: 'user-a' } - ) + expect((await sql`SELECT user_id FROM workspace_files`)[0]).toEqual({ user_id: 'user-a' }) await expect(sql`DELETE FROM project WHERE id = 'project-a'`).rejects.toMatchObject({ code: '23503', }) } ) - for (const isolation of ['read committed', 'repeatable read'] as const) { + for (const context of ['workspace', 'project'] as const) { + for (const isolation of ['read committed', 'repeatable read'] as const) { + check( + `queued ${isolation} creator deletion preserves a committed concurrent ${context} file or retries`, + async () => { + const snapshot = createDeferred() + const startDelete = createDeferred() + const deletePid = createDeferred() + const inserted = createDeferred() + const releaseInsert = createDeferred() + const deletion = sql.begin(`isolation level ${isolation}`, async (tx) => { + const [connection] = await tx`SELECT pg_backend_pid() AS pid, count(*) FROM "user"` + deletePid.resolve(connection.pid) + snapshot.resolve() + await startDelete.promise + await tx`DELETE FROM "user" WHERE id = 'user-a'` + }) + const deletionOutcome = deletion.then( + () => ({ code: null }), + (error: unknown) => ({ code: (error as { code: string }).code }) + ) + await snapshot.promise + const insertion = sql.begin(async (tx) => { + await tx`INSERT INTO workspace_files (id, context, user_id, workspace_id, project_id) + VALUES ('file', ${context}, 'user-a', ${context === 'workspace' ? 'workspace-a' : null}, ${context === 'project' ? 'project-a' : null})` + inserted.resolve() + await releaseInsert.promise + }) + await Promise.race([insertion, inserted.promise]) + startDelete.resolve() + try { + await waitForDatabaseLock(await deletePid.promise) + } finally { + releaseInsert.resolve() + } + await insertion + const outcome = await deletionOutcome + expect(outcome.code).toBe(isolation === 'repeatable read' ? '40001' : null) + expect((await sql`SELECT user_id FROM workspace_files`)[0]).toEqual({ + user_id: isolation === 'repeatable read' ? 'user-a' : null, + }) + } + ) + } + check( - `queued ${isolation} creator deletion preserves a committed concurrent Project file or retries`, + `a ${context} insert queued behind creator deletion cannot resurrect a missing creator`, async () => { - const snapshot = createDeferred() - const startDelete = createDeferred() - const deletePid = createDeferred() - const inserted = createDeferred() - const releaseInsert = createDeferred() - const deletion = sql.begin(`isolation level ${isolation}`, async (tx) => { - const [connection] = await tx`SELECT pg_backend_pid() AS pid, count(*) FROM "user"` - deletePid.resolve(connection.pid) - snapshot.resolve() - await startDelete.promise + const deleted = createDeferred() + const releaseDelete = createDeferred() + const insertPid = createDeferred() + const deletion = sql.begin(async (tx) => { await tx`DELETE FROM "user" WHERE id = 'user-a'` + deleted.resolve() + await releaseDelete.promise + }) + await Promise.race([deletion, deleted.promise]) + const insertion = sql.begin(async (tx) => { + const [connection] = await tx`SELECT pg_backend_pid() AS pid` + insertPid.resolve(connection.pid) + await tx`INSERT INTO workspace_files (id, context, user_id, workspace_id, project_id) + VALUES ('file', ${context}, 'user-a', ${context === 'workspace' ? 'workspace-a' : null}, ${context === 'project' ? 'project-a' : null})` }) - const deletionOutcome = deletion.then( + const insertionOutcome = insertion.then( () => ({ code: null }), (error: unknown) => ({ code: (error as { code: string }).code }) ) - await snapshot.promise - const insertion = sql.begin(async (tx) => { - await tx`INSERT INTO workspace_files (id, context, user_id, project_id) - VALUES ('file', 'project', 'user-a', 'project-a')` - inserted.resolve() - await releaseInsert.promise + try { + await waitForDatabaseLock(await insertPid.promise) + } finally { + releaseDelete.resolve() + } + await deletion + expect((await insertionOutcome).code).toBe('23503') + expect(await sql`SELECT id FROM workspace_files`).toHaveLength(0) + } + ) + check( + `${context} content write racing creator deletion retains the committed content`, + async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, project_id) + VALUES ('file', ${context}, 'user-a', ${context === 'workspace' ? 'workspace-a' : null}, ${context === 'project' ? 'project-a' : null})` + const updated = createDeferred() + const releaseUpdate = createDeferred() + const deletePid = createDeferred() + const update = sql.begin(async (tx) => { + await tx`UPDATE workspace_files SET key = 'committed-content' WHERE id = 'file'` + updated.resolve() + await releaseUpdate.promise + }) + await Promise.race([update, updated.promise]) + const deletion = sql.begin(async (tx) => { + const [connection] = await tx`SELECT pg_backend_pid() AS pid` + deletePid.resolve(connection.pid) + await tx`DELETE FROM "user" WHERE id = 'user-a'` }) - await Promise.race([insertion, inserted.promise]) - startDelete.resolve() try { await waitForDatabaseLock(await deletePid.promise) } finally { - releaseInsert.resolve() + releaseUpdate.resolve() } - await insertion - const outcome = await deletionOutcome - expect(outcome.code).toBe(isolation === 'repeatable read' ? '40001' : null) - expect( - (await sql`SELECT user_id, original_creator_user_id FROM workspace_files`)[0] - ).toEqual({ - user_id: isolation === 'repeatable read' ? 'user-a' : null, - original_creator_user_id: 'user-a', - }) + await Promise.all([update, deletion]) + expect(await sql`SELECT user_id, key FROM workspace_files`).toEqual([ + { user_id: null, key: 'committed-content' }, + ]) } ) } - - check( - 'a Project insert queued behind creator deletion cannot resurrect a missing creator', - async () => { - const deleted = createDeferred() - const releaseDelete = createDeferred() - const insertPid = createDeferred() - const deletion = sql.begin(async (tx) => { - await tx`DELETE FROM "user" WHERE id = 'user-a'` - deleted.resolve() - await releaseDelete.promise - }) - await Promise.race([deletion, deleted.promise]) - const insertion = sql.begin(async (tx) => { - const [connection] = await tx`SELECT pg_backend_pid() AS pid` - insertPid.resolve(connection.pid) - await tx`INSERT INTO workspace_files (id, context, user_id, project_id) - VALUES ('file', 'project', 'user-a', 'project-a')` - }) - const insertionOutcome = insertion.then( - () => ({ code: null }), - (error: unknown) => ({ code: (error as { code: string }).code }) - ) - try { - await waitForDatabaseLock(await insertPid.promise) - } finally { - releaseDelete.resolve() - } - await deletion - expect((await insertionOutcome).code).toBe('23503') - expect(await sql`SELECT id FROM workspace_files`).toHaveLength(0) - } - ) }) diff --git a/packages/db/migrations/0400_file_creator_lifetime.sql b/packages/db/migrations/0400_file_creator_lifetime.sql index b39c357aa08..be70949e373 100644 --- a/packages/db/migrations/0400_file_creator_lifetime.sql +++ b/packages/db/migrations/0400_file_creator_lifetime.sql @@ -1,75 +1,49 @@ -ALTER TABLE "folder" ADD COLUMN IF NOT EXISTS "original_creator_user_id" text;--> statement-breakpoint -ALTER TABLE "workspace_files" ADD COLUMN IF NOT EXISTS "original_creator_user_id" text;--> statement-breakpoint +DROP TRIGGER IF EXISTS project_file_uploader_delete_guard ON "user";--> statement-breakpoint +DROP FUNCTION IF EXISTS project_file_uploader_delete_guard();--> statement-breakpoint ALTER TABLE "folder" ALTER COLUMN "user_id" DROP NOT NULL;--> statement-breakpoint ALTER TABLE "workspace_files" ALTER COLUMN "user_id" DROP NOT NULL;--> statement-breakpoint DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_creator_lifetime_check' AND conrelid = 'folder'::regclass) THEN ALTER TABLE folder ADD CONSTRAINT folder_creator_lifetime_check CHECK ( - user_id IS NOT NULL OR coalesce(project_id IS NOT NULL AND char_length(original_creator_user_id) > 0, false) + user_id IS NOT NULL OR (resource_type = 'file' AND num_nonnulls(workspace_id, project_id) = 1) ) NOT VALID; END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'workspace_files_creator_lifetime_check' AND conrelid = 'workspace_files'::regclass) THEN ALTER TABLE workspace_files ADD CONSTRAINT workspace_files_creator_lifetime_check CHECK ( - user_id IS NOT NULL OR coalesce(project_id IS NOT NULL AND char_length(original_creator_user_id) > 0, false) + user_id IS NOT NULL OR (context IN ('workspace', 'project') AND num_nonnulls(workspace_id, project_id) = 1 AND organization_id IS NULL) ) NOT VALID; END IF; END $$;--> statement-breakpoint -CREATE OR REPLACE FUNCTION project_file_creator_snapshot() +CREATE OR REPLACE FUNCTION file_require_creator() RETURNS trigger LANGUAGE plpgsql AS $$ -DECLARE - original_creator text; BEGIN - IF TG_OP = 'INSERT' THEN - IF NEW.user_id IS NULL THEN - RAISE EXCEPTION 'New file resources require their actual creator' USING ERRCODE = '23514'; - END IF; - IF NEW.project_id IS NOT NULL THEN - IF NEW.original_creator_user_id IS NOT NULL AND NEW.original_creator_user_id IS DISTINCT FROM NEW.user_id THEN - RAISE EXCEPTION 'Project creator snapshot must match its actual creator' USING ERRCODE = '23514'; - END IF; - NEW.original_creator_user_id := NEW.user_id; - ELSIF NEW.original_creator_user_id IS NOT NULL THEN - RAISE EXCEPTION 'Creator snapshots are reserved for Project resources' USING ERRCODE = '23514'; - END IF; - ELSE - original_creator := coalesce(OLD.original_creator_user_id, - CASE WHEN OLD.project_id IS NOT NULL OR NEW.project_id IS NOT NULL THEN OLD.user_id ELSE NULL END); - IF original_creator IS NOT NULL THEN - IF (OLD.original_creator_user_id IS NOT NULL AND NEW.original_creator_user_id IS DISTINCT FROM OLD.original_creator_user_id) - OR (NEW.original_creator_user_id IS NOT NULL AND NEW.original_creator_user_id IS DISTINCT FROM original_creator) - OR (NEW.user_id IS NOT NULL AND NEW.user_id IS DISTINCT FROM original_creator) THEN - RAISE EXCEPTION 'Project original creator attribution cannot be changed' USING ERRCODE = '23514'; - END IF; - NEW.original_creator_user_id := original_creator; - ELSIF NEW.original_creator_user_id IS NOT NULL THEN - RAISE EXCEPTION 'Project original creator attribution is unavailable' USING ERRCODE = '23514'; - END IF; - END IF; - IF NEW.user_id IS NULL AND (NEW.project_id IS NULL - OR NEW.original_creator_user_id IS NULL OR NEW.original_creator_user_id = '') THEN - RAISE EXCEPTION 'Only attributed Project resources may outlive their creator account' USING ERRCODE = '23514'; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'New file resources require their actual creator' USING ERRCODE = '23514'; END IF; RETURN NEW; END; $$;--> statement-breakpoint -DROP TRIGGER IF EXISTS folder_creator_lifetime ON folder;--> statement-breakpoint -CREATE TRIGGER folder_creator_lifetime -BEFORE INSERT OR UPDATE OF user_id, original_creator_user_id, project_id ON folder -FOR EACH ROW EXECUTE FUNCTION project_file_creator_snapshot();--> statement-breakpoint -DROP TRIGGER IF EXISTS workspace_files_creator_lifetime ON workspace_files;--> statement-breakpoint -CREATE TRIGGER workspace_files_creator_lifetime -BEFORE INSERT OR UPDATE OF user_id, original_creator_user_id, project_id ON workspace_files -FOR EACH ROW EXECUTE FUNCTION project_file_creator_snapshot();--> statement-breakpoint -CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() +DROP TRIGGER IF EXISTS folder_require_creator ON folder;--> statement-breakpoint +CREATE TRIGGER folder_require_creator +BEFORE INSERT ON folder +FOR EACH ROW EXECUTE FUNCTION file_require_creator();--> statement-breakpoint +DROP TRIGGER IF EXISTS workspace_files_require_creator ON workspace_files;--> statement-breakpoint +CREATE TRIGGER workspace_files_require_creator +BEFORE INSERT ON workspace_files +FOR EACH ROW EXECUTE FUNCTION file_require_creator();--> statement-breakpoint +CREATE OR REPLACE FUNCTION shared_file_creator_delete() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN - -- Retain original authorship without substituting a new owner, payer, or creator. - UPDATE workspace_files - SET original_creator_user_id = coalesce(original_creator_user_id, user_id), user_id = NULL - WHERE project_id IS NOT NULL AND user_id = OLD.id; - UPDATE folder - SET original_creator_user_id = coalesce(original_creator_user_id, user_id), user_id = NULL - WHERE project_id IS NOT NULL AND user_id = OLD.id; + UPDATE workspace_files SET user_id = NULL + WHERE user_id = OLD.id AND context IN ('workspace', 'project') + AND num_nonnulls(workspace_id, project_id) = 1 AND organization_id IS NULL; + UPDATE folder SET user_id = NULL + WHERE user_id = OLD.id AND resource_type = 'file' + AND num_nonnulls(workspace_id, project_id) = 1; RETURN OLD; END; $$;--> statement-breakpoint +DROP TRIGGER IF EXISTS shared_file_creator_delete ON "user";--> statement-breakpoint +CREATE TRIGGER shared_file_creator_delete +BEFORE DELETE ON "user" +FOR EACH ROW EXECUTE FUNCTION shared_file_creator_delete(); diff --git a/packages/db/migrations/meta/0400_snapshot.json b/packages/db/migrations/meta/0400_snapshot.json index 12da7f26025..ff5cd39d301 100644 --- a/packages/db/migrations/meta/0400_snapshot.json +++ b/packages/db/migrations/meta/0400_snapshot.json @@ -8553,12 +8553,6 @@ "primaryKey": false, "notNull": false }, - "original_creator_user_id": { - "name": "original_creator_user_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "workspace_id": { "name": "workspace_id", "type": "text", @@ -8847,7 +8841,7 @@ "checkConstraints": { "folder_creator_lifetime_check": { "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"project_id\" IS NOT NULL AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + "value": "\"folder\".\"user_id\" IS NOT NULL OR (\"folder\".\"resource_type\" = 'file' AND num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1)" }, "folder_owner_check": { "name": "folder_owner_check", @@ -28233,12 +28227,6 @@ "primaryKey": false, "notNull": false }, - "original_creator_user_id": { - "name": "original_creator_user_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "workspace_id": { "name": "workspace_id", "type": "text", @@ -28694,7 +28682,7 @@ "checkConstraints": { "workspace_files_creator_lifetime_check": { "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"project_id\" IS NOT NULL AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR (\"workspace_files\".\"context\" IN ('workspace', 'project') AND num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\") = 1 AND \"workspace_files\".\"organization_id\" IS NULL)" }, "workspace_files_organization_binding_check": { "name": "workspace_files_organization_binding_check", diff --git a/packages/db/migrations/meta/0401_snapshot.json b/packages/db/migrations/meta/0401_snapshot.json index 7b81132c808..7257df72c3b 100644 --- a/packages/db/migrations/meta/0401_snapshot.json +++ b/packages/db/migrations/meta/0401_snapshot.json @@ -8553,12 +8553,6 @@ "primaryKey": false, "notNull": false }, - "original_creator_user_id": { - "name": "original_creator_user_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "workspace_id": { "name": "workspace_id", "type": "text", @@ -8847,7 +8841,7 @@ "checkConstraints": { "folder_creator_lifetime_check": { "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"project_id\" IS NOT NULL AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + "value": "\"folder\".\"user_id\" IS NOT NULL OR (\"folder\".\"resource_type\" = 'file' AND num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1)" }, "folder_owner_check": { "name": "folder_owner_check", @@ -28233,12 +28227,6 @@ "primaryKey": false, "notNull": false }, - "original_creator_user_id": { - "name": "original_creator_user_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "workspace_id": { "name": "workspace_id", "type": "text", @@ -28694,7 +28682,7 @@ "checkConstraints": { "workspace_files_creator_lifetime_check": { "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"project_id\" IS NOT NULL AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR (\"workspace_files\".\"context\" IN ('workspace', 'project') AND num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\") = 1 AND \"workspace_files\".\"organization_id\" IS NULL)" }, "workspace_files_organization_binding_check": { "name": "workspace_files_organization_binding_check", diff --git a/packages/db/migrations/meta/0402_snapshot.json b/packages/db/migrations/meta/0402_snapshot.json index 06ad3ee721c..26f2b8b91a1 100644 --- a/packages/db/migrations/meta/0402_snapshot.json +++ b/packages/db/migrations/meta/0402_snapshot.json @@ -8553,12 +8553,6 @@ "primaryKey": false, "notNull": false }, - "original_creator_user_id": { - "name": "original_creator_user_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "workspace_id": { "name": "workspace_id", "type": "text", @@ -8847,7 +8841,7 @@ "checkConstraints": { "folder_creator_lifetime_check": { "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"project_id\" IS NOT NULL AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + "value": "\"folder\".\"user_id\" IS NOT NULL OR (\"folder\".\"resource_type\" = 'file' AND num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1)" }, "folder_owner_check": { "name": "folder_owner_check", @@ -28277,12 +28271,6 @@ "primaryKey": false, "notNull": false }, - "original_creator_user_id": { - "name": "original_creator_user_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "workspace_id": { "name": "workspace_id", "type": "text", @@ -28738,7 +28726,7 @@ "checkConstraints": { "workspace_files_creator_lifetime_check": { "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"project_id\" IS NOT NULL AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR (\"workspace_files\".\"context\" IN ('workspace', 'project') AND num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\") = 1 AND \"workspace_files\".\"organization_id\" IS NULL)" }, "workspace_files_organization_binding_check": { "name": "workspace_files_organization_binding_check", diff --git a/packages/db/migrations/meta/0403_snapshot.json b/packages/db/migrations/meta/0403_snapshot.json index f705553497d..c4d3466654d 100644 --- a/packages/db/migrations/meta/0403_snapshot.json +++ b/packages/db/migrations/meta/0403_snapshot.json @@ -8716,12 +8716,6 @@ "primaryKey": false, "notNull": false }, - "original_creator_user_id": { - "name": "original_creator_user_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "workspace_id": { "name": "workspace_id", "type": "text", @@ -9010,7 +9004,7 @@ "checkConstraints": { "folder_creator_lifetime_check": { "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR coalesce(\"folder\".\"project_id\" IS NOT NULL AND char_length(\"folder\".\"original_creator_user_id\") > 0, false)" + "value": "\"folder\".\"user_id\" IS NOT NULL OR (\"folder\".\"resource_type\" = 'file' AND num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1)" }, "folder_owner_check": { "name": "folder_owner_check", @@ -28614,12 +28608,6 @@ "primaryKey": false, "notNull": false }, - "original_creator_user_id": { - "name": "original_creator_user_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, "workspace_id": { "name": "workspace_id", "type": "text", @@ -29103,7 +29091,7 @@ "checkConstraints": { "workspace_files_creator_lifetime_check": { "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR coalesce(\"workspace_files\".\"project_id\" IS NOT NULL AND char_length(\"workspace_files\".\"original_creator_user_id\") > 0, false)" + "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR (\"workspace_files\".\"context\" IN ('workspace', 'project') AND num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\") = 1 AND \"workspace_files\".\"organization_id\" IS NULL)" }, "workspace_files_organization_binding_check": { "name": "workspace_files_organization_binding_check", diff --git a/packages/db/schema.ts b/packages/db/schema.ts index e6b21f8c094..56d93744e23 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -235,10 +235,8 @@ export const folder = pgTable( }), resourceType: folderResourceTypeEnum('resource_type').notNull(), name: text('name').notNull(), - /** Project creator references clear on account deletion; legacy folders retain their cascade. */ + /** Durable file folders outlive their creator; other folder types retain their cascade. */ userId: text('user_id').references(() => user.id, { onDelete: 'cascade' }), - /** Immutable original Project creator, retained like file-version author IDs after account deletion. */ - originalCreatorUserId: text('original_creator_user_id'), workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'cascade' }), parentId: text('parent_id').references((): AnyPgColumn => folder.id, { onDelete: 'set null', @@ -252,7 +250,7 @@ export const folder = pgTable( (table) => ({ creatorLifetimeCheck: check( 'folder_creator_lifetime_check', - sql`${table.userId} IS NOT NULL OR coalesce(${table.projectId} IS NOT NULL AND char_length(${table.originalCreatorUserId}) > 0, false)` + sql`${table.userId} IS NOT NULL OR (${table.resourceType} = 'file' AND num_nonnulls(${table.workspaceId}, ${table.projectId}) = 1)` ), ownerCheck: check( 'folder_owner_check', @@ -2498,10 +2496,8 @@ export const workspaceFiles = pgTable( /** Direct owner, independent of creator, caller, and billing payer. */ projectId: text('project_id').references(() => project.id, { onDelete: 'restrict' }), key: text('key').notNull(), - /** Project creator references clear on account deletion; other file contexts retain their cascade. */ + /** Durable shared files outlive their creator; other contexts retain their cascade. */ userId: text('user_id').references(() => user.id, { onDelete: 'cascade' }), - /** Immutable original Project creator, independent of the current lifecycle owner and payer. */ - originalCreatorUserId: text('original_creator_user_id'), workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'cascade' }), organizationId: text('organization_id').references(() => organization.id, { onDelete: 'cascade', @@ -2581,7 +2577,7 @@ export const workspaceFiles = pgTable( (table) => ({ creatorLifetimeCheck: check( 'workspace_files_creator_lifetime_check', - sql`${table.userId} IS NOT NULL OR coalesce(${table.projectId} IS NOT NULL AND char_length(${table.originalCreatorUserId}) > 0, false)` + sql`${table.userId} IS NOT NULL OR (${table.context} IN ('workspace', 'project') AND num_nonnulls(${table.workspaceId}, ${table.projectId}) = 1 AND ${table.organizationId} IS NULL)` ), ownerCheck: check( 'workspace_files_owner_check', diff --git a/packages/db/script-migrations/0032_backfill_project_file_creators.ts b/packages/db/script-migrations/0032_backfill_project_file_creators.ts deleted file mode 100644 index 949bcb03b9b..00000000000 --- a/packages/db/script-migrations/0032_backfill_project_file_creators.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { ScriptMigration } from '@sim/db/script-migrations/types' -import { createLogger } from '@sim/logger' -import type { Sql } from 'postgres' - -const logger = createLogger('ProjectFileCreatorBackfill') -const BATCH_SIZE = 500 - -async function backfillTable(sql: Sql, table: 'workspace_files' | 'folder'): Promise { - let afterId = '' - let backfilled = 0 - for (;;) { - const rows = await sql<{ id: string }[]>` - SELECT id FROM ${sql(table)} - WHERE project_id IS NOT NULL AND original_creator_user_id IS NULL AND user_id IS NOT NULL - AND id > ${afterId} - ORDER BY id LIMIT ${BATCH_SIZE} - ` - if (!rows.length) return backfilled - afterId = rows[rows.length - 1].id - const ids = rows.map((row) => row.id) - const updated = await sql.begin(async (tx) => { - await tx`SET LOCAL lock_timeout = '5s'` - await tx`SET LOCAL statement_timeout = '30s'` - await tx`SELECT id FROM ${tx(table)} WHERE id = ANY(${ids}::text[]) ORDER BY id FOR UPDATE` - return tx<{ id: string }[]>` - UPDATE ${tx(table)} SET original_creator_user_id = user_id - WHERE id = ANY(${ids}::text[]) AND project_id IS NOT NULL - AND original_creator_user_id IS NULL AND user_id IS NOT NULL - RETURNING id - ` - }) - backfilled += updated.length - } -} - -/** Snapshots canonical Project creators without changing live references, ownership, or content. */ -export async function backfillProjectFileCreators(sql: Sql) { - return { - files: await backfillTable(sql, 'workspace_files'), - folders: await backfillTable(sql, 'folder'), - } -} - -export const backfillProjectFileCreatorsMigration: ScriptMigration = { - name: '0032_backfill_project_file_creators', - async up(sql) { - logger.info('Project file creator backfill completed', await backfillProjectFileCreators(sql)) - }, -} diff --git a/packages/db/script-migrations/index.ts b/packages/db/script-migrations/index.ts index d510112c258..10c18997c5a 100644 --- a/packages/db/script-migrations/index.ts +++ b/packages/db/script-migrations/index.ts @@ -11,7 +11,6 @@ import { knowledgeProjectionAsyncMigration } from '@sim/db/script-migrations/002 import { scopeKeywordProjectionsMigration } from '@sim/db/script-migrations/0025_scope_keyword_projections' import { userTableSchemaForWriteMigration } from '@sim/db/script-migrations/0026_user_table_schema_for_write' import { validateFileOwnershipMigration } from '@sim/db/script-migrations/0031_validate_file_ownership' -import { backfillProjectFileCreatorsMigration } from '@sim/db/script-migrations/0032_backfill_project_file_creators' import { backfillPublicShareEntitiesMigration } from '@sim/db/script-migrations/0033_backfill_public_share_entities' import type { Sql } from 'postgres' import { backfillTableOrderKeys } from './0001_backfill_table_order_keys' @@ -66,7 +65,6 @@ export const scriptMigrations: readonly ScriptMigration[] = [ * run `packages/db/scripts/retire-indexed-search.ts --help` for usage. */ validateFileOwnershipMigration, - backfillProjectFileCreatorsMigration, backfillPublicShareEntitiesMigration, ] diff --git a/packages/db/scripts/reconcile-file-ownership.ts b/packages/db/scripts/reconcile-file-ownership.ts index ddfef0dc1cc..caf901ecc7a 100644 --- a/packages/db/scripts/reconcile-file-ownership.ts +++ b/packages/db/scripts/reconcile-file-ownership.ts @@ -24,7 +24,7 @@ try { for (const chunk of source.split('--> statement-breakpoint')) { const statement = chunk.replace(/^(?:\s|--[^\n]*(?:\n|$))*/, '') if ( - !/^(?:CREATE (?:OR REPLACE FUNCTION|(?:CONSTRAINT )?TRIGGER)|DROP TRIGGER)\b/i.test( + !/^(?:CREATE (?:OR REPLACE FUNCTION|(?:CONSTRAINT )?TRIGGER)|DROP (?:TRIGGER|FUNCTION))\b/i.test( statement ) ) diff --git a/packages/sim-cli/src/generated/v2-api.ts b/packages/sim-cli/src/generated/v2-api.ts index 84efd2e93f6..1b2b5144a67 100644 --- a/packages/sim-cli/src/generated/v2-api.ts +++ b/packages/sim-cli/src/generated/v2-api.ts @@ -31,7 +31,7 @@ type AbortFileUploadResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -1522,7 +1522,7 @@ type CompleteFileUploadResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -1833,7 +1833,7 @@ type CreateFileResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -1887,7 +1887,7 @@ type CreateFileUploadResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -4826,7 +4826,7 @@ type EditFileContentResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -5406,7 +5406,7 @@ type GetFileResponseRef1 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -5465,7 +5465,7 @@ type GetFileUploadResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -7879,7 +7879,7 @@ type ListFilesResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -11413,7 +11413,7 @@ type RenameFileResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -11831,7 +11831,7 @@ type RestoreFileResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -12108,7 +12108,7 @@ type RevertFileVersionResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null @@ -12825,7 +12825,7 @@ type UpdateFileContentResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string + uploadedByEmail: string | null uploadedAt: string updatedAt: string deletedAt: string | null diff --git a/packages/testing/src/mocks/schema-tables.generated.ts b/packages/testing/src/mocks/schema-tables.generated.ts index 00fc49eae96..54a1076fdd0 100644 --- a/packages/testing/src/mocks/schema-tables.generated.ts +++ b/packages/testing/src/mocks/schema-tables.generated.ts @@ -57,7 +57,6 @@ export const GENERATED_SCHEMA_TABLES = { 'resourceType', 'name', 'userId', - 'originalCreatorUserId', 'workspaceId', 'parentId', 'locked', @@ -606,7 +605,6 @@ export const GENERATED_SCHEMA_TABLES = { 'projectId', 'key', 'userId', - 'originalCreatorUserId', 'workspaceId', 'organizationId', 'folderId', From 341a721b5b672cce3aa0f335c31f2f25d9d54c9a Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Wed, 7 Oct 2026 15:49:51 -0700 Subject: [PATCH 033/113] fix(files): reconcile creator handoff with project ownership Keep shared file and folder creator references non-null by transferring them to validated successors during departure and account deletion. Discover Project-only references from prior departures, retain attribution while an environment still grants access, and transfer ownership and storage payers atomically when the final access is removed. Recheck the selected membership under lock before handoff so a concurrent replacement grant and its creator attribution remain untouched. Reconcile the unreleased creator migration and restore non-null API/CLI contracts. Integrates lifecycle 817191c757e98ef6cec60a1727b1bf52bddb5d43 and membership race fix be01225427107ba93543649fe3537289bc5f57f3 into the file foundation. --- apps/docs/openapi-v2-files-audit.json | 60 +- .../app/api/files/uploads/finalizers.test.ts | 2 +- .../[id]/members/[memberId]/route.ts | 34 +- .../v1/admin/workspaces/[id]/members/route.ts | 31 +- .../v2/files/[fileId]/content/route.test.ts | 4 +- .../v2/files/[fileId]/metadata/route.test.ts | 4 +- .../app/api/v2/files/[fileId]/route.test.ts | 4 +- .../versions/[version]/revert/route.test.ts | 3 +- apps/sim/app/api/v2/files/route.test.ts | 4 +- apps/sim/app/api/v2/files/utils.ts | 26 +- apps/sim/lib/api/contracts/v2/files.ts | 3 +- .../api/contracts/workspace-file-folders.ts | 2 +- apps/sim/lib/api/contracts/workspace-files.ts | 2 +- .../membership-external-removal.test.ts | 6 +- .../lib/billing/organizations/membership.ts | 61 +- .../billing/organizations/resource-handoff.ts | 107 ++ .../file-round-trip-provenance.test.ts | 2 +- apps/sim/lib/projects/account-deletion.ts | 86 +- apps/sim/lib/projects/membership.ts | 2 +- apps/sim/lib/projects/resource-handoff.ts | 277 ++++++ .../creator-lifetime.integration.ts | 107 +- .../workspace-file-folder-manager.ts | 4 +- .../workspace/workspace-file-manager.ts | 2 +- .../workspace/workspace-file-versions.ts | 2 +- apps/sim/lib/users/account-deletion.ts | 150 ++- .../read-workspace-file-secret-provenance.ts | 2 +- .../shared-resource-handoff.integration.ts | 937 ++++++++++++++++++ .../lib/workspaces/access/workspace-access.ts | 79 +- apps/sim/lib/workspaces/resource-handoff.ts | 196 ++++ .../test-shared-resource-lifecycle-e2e.ts | 429 ++++++++ .../db/file-creator-lifetime.integration.ts | 255 +---- .../migrations/0400_file_creator_lifetime.sql | 49 +- .../db/migrations/meta/0400_snapshot.json | 12 +- .../db/migrations/meta/0401_snapshot.json | 12 +- .../db/migrations/meta/0402_snapshot.json | 12 +- .../db/migrations/meta/0403_snapshot.json | 12 +- packages/db/schema.ts | 18 +- packages/sim-cli/src/generated/v2-api.ts | 24 +- 38 files changed, 2380 insertions(+), 642 deletions(-) create mode 100644 apps/sim/lib/billing/organizations/resource-handoff.ts create mode 100644 apps/sim/lib/projects/resource-handoff.ts create mode 100644 apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts create mode 100644 apps/sim/lib/workspaces/resource-handoff.ts create mode 100644 apps/sim/scripts/test-shared-resource-lifecycle-e2e.ts diff --git a/apps/docs/openapi-v2-files-audit.json b/apps/docs/openapi-v2-files-audit.json index 7b335cb5e4f..bc682bdbbe7 100644 --- a/apps/docs/openapi-v2-files-audit.json +++ b/apps/docs/openapi-v2-files-audit.json @@ -3948,17 +3948,10 @@ "maxLength": 4096 }, "uploadedByEmail": { - "anyOf": [ - { - "type": "string", - "format": "email", - "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" - }, - { - "type": "null" - } - ], - "description": "Current email address of the creator, or null after account deletion.", + "type": "string", + "format": "email", + "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", + "description": "Current email address of the uploader.", "examples": ["jane@example.com"] }, "uploadedAt": { @@ -4092,17 +4085,10 @@ "maxLength": 4096 }, "uploadedByEmail": { - "anyOf": [ - { - "type": "string", - "format": "email", - "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" - }, - { - "type": "null" - } - ], - "description": "Current email address of the creator, or null after account deletion.", + "type": "string", + "format": "email", + "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", + "description": "Current email address of the uploader.", "examples": ["jane@example.com"] }, "uploadedAt": { @@ -5435,17 +5421,10 @@ "maxLength": 4096 }, "uploadedByEmail": { - "anyOf": [ - { - "type": "string", - "format": "email", - "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" - }, - { - "type": "null" - } - ], - "description": "Current email address of the creator, or null after account deletion.", + "type": "string", + "format": "email", + "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", + "description": "Current email address of the uploader.", "examples": ["jane@example.com"] }, "uploadedAt": { @@ -6345,17 +6324,10 @@ "maxLength": 4096 }, "uploadedByEmail": { - "anyOf": [ - { - "type": "string", - "format": "email", - "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" - }, - { - "type": "null" - } - ], - "description": "Current email address of the creator, or null after account deletion.", + "type": "string", + "format": "email", + "pattern": "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", + "description": "Current email address of the uploader.", "examples": ["jane@example.com"] }, "uploadedAt": { diff --git a/apps/sim/app/api/files/uploads/finalizers.test.ts b/apps/sim/app/api/files/uploads/finalizers.test.ts index 748c59cdfef..5545692a4c8 100644 --- a/apps/sim/app/api/files/uploads/finalizers.test.ts +++ b/apps/sim/app/api/files/uploads/finalizers.test.ts @@ -36,7 +36,7 @@ import { } from '@/app/api/files/uploads/finalizers' import type { InternalUploadPurpose } from '@/app/api/files/uploads/purposes' -usersQueriesMockFns.mockFindUserEmailsByIds.mockImplementation( +usersQueriesMockFns.mockGetUserEmailsByIds.mockImplementation( async () => new Map([['user-1', 'ada@example.com']]) ) diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts index be48377a5ca..8cd732d3cc4 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts @@ -33,14 +33,9 @@ import { } from '@/lib/api/contracts/v1/admin' import { parseRequest } from '@/lib/api/server' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' -import { revokeWorkspaceCredentialMembershipsTx } from '@/lib/credentials/access' -import { removeWorkspaceSkillMembershipsTx } from '@/lib/skills/access' +import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access' import { getWorkspaceById } from '@/lib/workspaces/permissions/utils' -import { - reassignWorkflowOwnershipForWorkspaceMemberRemovalTx, - transferWorkspaceOwnershipToBilledAccountForMemberRemovalTx, - WorkspaceBillingAccountRemovalError, -} from '@/lib/workspaces/utils' +import { WorkspaceBillingAccountRemovalError } from '@/lib/workspaces/utils' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { badRequestResponse, @@ -278,28 +273,19 @@ export const DELETE = withRouteHandler( ) } - await db.transaction(async (tx) => { - await transferWorkspaceOwnershipToBilledAccountForMemberRemovalTx({ - tx, + const removed = await db.transaction(async (tx) => { + const result = await revokeWorkspaceAccessTx(tx, { workspaceId, - departingUserId: existingMember.userId, + userId: existingMember.userId, + expectedPermissionId: existingMember.id, }) - - const workflowOwnershipReassignment = - await reassignWorkflowOwnershipForWorkspaceMemberRemovalTx({ - tx, - workspaceIds: [workspaceId], - departingUserId: existingMember.userId, - }) - if (workflowOwnershipReassignment.unresolved.length > 0) { + if (!result.revoked) { + if (result.reason === 'membership-changed') return false throw new WorkspaceBillingAccountRemovalError() } - - await tx.delete(permissions).where(eq(permissions.id, memberId)) - - await revokeWorkspaceCredentialMembershipsTx(tx, workspaceId, existingMember.userId) - await removeWorkspaceSkillMembershipsTx(tx, workspaceId, existingMember.userId) + return true }) + if (!removed) return notFoundResponse('Workspace member') logger.info(`Admin API: Removed member ${memberId} from workspace ${workspaceId}`, { userId: existingMember.userId, diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts index 1b3b429a9d3..0f5682492a6 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts @@ -49,15 +49,10 @@ import { } from '@/lib/api/contracts/v1/admin' import { parseRequest } from '@/lib/api/server' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' -import { revokeWorkspaceCredentialMembershipsTx } from '@/lib/credentials/access' import { syncWorkspaceEnvCredentials } from '@/lib/credentials/environment' -import { removeWorkspaceSkillMembershipsTx } from '@/lib/skills/access' +import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access' import { getWorkspaceById } from '@/lib/workspaces/permissions/utils' -import { - reassignWorkflowOwnershipForWorkspaceMemberRemovalTx, - transferWorkspaceOwnershipToBilledAccountForMemberRemovalTx, - WorkspaceBillingAccountRemovalError, -} from '@/lib/workspaces/utils' +import { WorkspaceBillingAccountRemovalError } from '@/lib/workspaces/utils' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { badRequestResponse, @@ -395,26 +390,8 @@ export const DELETE = withRouteHandler( } await db.transaction(async (tx) => { - await transferWorkspaceOwnershipToBilledAccountForMemberRemovalTx({ - tx, - workspaceId, - departingUserId: userId, - }) - - const workflowOwnershipReassignment = - await reassignWorkflowOwnershipForWorkspaceMemberRemovalTx({ - tx, - workspaceIds: [workspaceId], - departingUserId: userId, - }) - if (workflowOwnershipReassignment.unresolved.length > 0) { - throw new WorkspaceBillingAccountRemovalError() - } - - await tx.delete(permissions).where(eq(permissions.id, existingPermission.id)) - - await revokeWorkspaceCredentialMembershipsTx(tx, workspaceId, userId) - await removeWorkspaceSkillMembershipsTx(tx, workspaceId, userId) + const result = await revokeWorkspaceAccessTx(tx, { workspaceId, userId: userId }) + if (!result.revoked) throw new WorkspaceBillingAccountRemovalError() }) logger.info(`Admin API: Removed user ${userId} from workspace ${workspaceId}`) diff --git a/apps/sim/app/api/v2/files/[fileId]/content/route.test.ts b/apps/sim/app/api/v2/files/[fileId]/content/route.test.ts index 0b6b7cfb709..bbdf598dfac 100644 --- a/apps/sim/app/api/v2/files/[fileId]/content/route.test.ts +++ b/apps/sim/app/api/v2/files/[fileId]/content/route.test.ts @@ -42,7 +42,7 @@ vi.mock('@/lib/users/queries', () => usersQueriesMock) import { NoWorkspaceAccessError } from '@/lib/core/application' import { PATCH, PUT } from '@/app/api/v2/files/[fileId]/content/route' -const { mockFindUserEmailsByIds } = usersQueriesMockFns +const { mockGetUserEmailsByIds } = usersQueriesMockFns const WORKSPACE_ID = 'workspace-1' const FILE_ID = 'wf_1' @@ -104,7 +104,7 @@ describe('PUT /api/v2/files/[fileId]/content', () => { mocks.admit.mockResolvedValue(undefined) mocks.editContent.mockResolvedValue({ file: record, lineCount: 1 }) mocks.updateContent.mockResolvedValue({ file: record }) - mockFindUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) + mockGetUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) }) it('performs authenticated admission before parsing a large or malformed body', async () => { diff --git a/apps/sim/app/api/v2/files/[fileId]/metadata/route.test.ts b/apps/sim/app/api/v2/files/[fileId]/metadata/route.test.ts index e7cc5b1b991..1534cc41728 100644 --- a/apps/sim/app/api/v2/files/[fileId]/metadata/route.test.ts +++ b/apps/sim/app/api/v2/files/[fileId]/metadata/route.test.ts @@ -29,7 +29,7 @@ import { NoWorkspaceAccessError } from '@/lib/core/application' import { OrchestrationError } from '@/lib/core/orchestration/types' import { GET } from '@/app/api/v2/files/[fileId]/metadata/route' -const { mockFindUserEmailsByIds } = usersQueriesMockFns +const { mockGetUserEmailsByIds } = usersQueriesMockFns const WORKSPACE_ID = 'workspace-1' const FILE_ID = 'wf_1' @@ -93,7 +93,7 @@ describe('GET /api/v2/files/[fileId]/metadata', () => { file: { ...buildRecord(), currentVersion: 3 }, share: SHARE, }) - mockFindUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) + mockGetUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) }) it('conceals cross-workspace authorization as not found', async () => { diff --git a/apps/sim/app/api/v2/files/[fileId]/route.test.ts b/apps/sim/app/api/v2/files/[fileId]/route.test.ts index 91318f24e8d..99835d09c11 100644 --- a/apps/sim/app/api/v2/files/[fileId]/route.test.ts +++ b/apps/sim/app/api/v2/files/[fileId]/route.test.ts @@ -47,7 +47,7 @@ import { NoWorkspaceAccessError } from '@/lib/core/application' import { OrchestrationError } from '@/lib/core/orchestration/types' import { GET, PATCH } from '@/app/api/v2/files/[fileId]/route' -const { mockFindUserEmailsByIds } = usersQueriesMockFns +const { mockGetUserEmailsByIds } = usersQueriesMockFns const WORKSPACE_ID = 'workspace-1' const FILE_ID = 'wf_1' @@ -103,7 +103,7 @@ describe('v2 single-file routes', () => { workspaceId: WORKSPACE_ID, deleted: true, }) - mockFindUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) + mockGetUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) mocks.authorizeDownload.mockResolvedValue(undefined) }) diff --git a/apps/sim/app/api/v2/files/[fileId]/versions/[version]/revert/route.test.ts b/apps/sim/app/api/v2/files/[fileId]/versions/[version]/revert/route.test.ts index 59fec1a820d..c6a62c7bcb1 100644 --- a/apps/sim/app/api/v2/files/[fileId]/versions/[version]/revert/route.test.ts +++ b/apps/sim/app/api/v2/files/[fileId]/versions/[version]/revert/route.test.ts @@ -28,7 +28,7 @@ vi.mock('@/lib/users/queries', () => usersQueriesMock) import { workspaceFileRevision } from '@/lib/workspace-files/application/file-revision' import { POST } from '@/app/api/v2/files/[fileId]/versions/[version]/revert/route' -const { mockFindUserEmailsByIds } = usersQueriesMockFns +const { mockGetUserEmailsByIds, mockFindUserEmailsByIds } = usersQueriesMockFns const WORKSPACE_ID = 'workspace-1' const FILE_ID = 'wf_1' @@ -93,6 +93,7 @@ describe('POST /api/v2/files/[fileId]/versions/[version]/revert', () => { reverted: true, revertedFrom: 3, }) + mockGetUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) mockFindUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) }) diff --git a/apps/sim/app/api/v2/files/route.test.ts b/apps/sim/app/api/v2/files/route.test.ts index 6587d0e58ab..e94b63a9cda 100644 --- a/apps/sim/app/api/v2/files/route.test.ts +++ b/apps/sim/app/api/v2/files/route.test.ts @@ -33,7 +33,7 @@ vi.mock('@/lib/users/queries', () => usersQueriesMock) import { GET, POST } from '@/app/api/v2/files/route' -const { mockFindUserEmailsByIds } = usersQueriesMockFns +const { mockGetUserEmailsByIds } = usersQueriesMockFns const mockQueryFiles = workspaceFilesListMockFns.mockQueryWorkspaceFilePage @@ -81,7 +81,7 @@ describe('/api/v2/files', () => { nextKeys: undefined, }) mocks.createFile.mockResolvedValue({ file: FILE }) - mockFindUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) + mockGetUserEmailsByIds.mockResolvedValue(new Map([['user-1', 'ada@example.com']])) }) /** diff --git a/apps/sim/app/api/v2/files/utils.ts b/apps/sim/app/api/v2/files/utils.ts index 7a3579b1f08..b7956dccee2 100644 --- a/apps/sim/app/api/v2/files/utils.ts +++ b/apps/sim/app/api/v2/files/utils.ts @@ -6,7 +6,11 @@ import { workspaceResourceWebUrl } from '@/lib/resources' import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace' import { workspaceFileVfsPath } from '@/lib/uploads/contexts/workspace/workspace-file-manager' import type { WorkspaceFileVersionRecord } from '@/lib/uploads/contexts/workspace/workspace-file-versions' -import { findUserEmailsByIds } from '@/lib/users/queries' +import { + findUserEmailsByIds, + getUserEmailsByIds, + requireResolvedUserEmail, +} from '@/lib/users/queries' import type { ReadWorkspaceFileTextResult } from '@/lib/workspace-files/application/read-workspace-file-text' import { parseWorkspaceFileFolderDisplayPath } from '@/lib/workspace-files/folder-display-path' @@ -18,7 +22,7 @@ import { parseWorkspaceFileFolderDisplayPath } from '@/lib/workspace-files/folde */ function serializeV2File( record: WorkspaceFileRecord, - uploadedByEmail: string | null, + uploadedByEmail: string, baseUrl: string ): V2File { const folderPath = record.folderId @@ -47,22 +51,20 @@ function serializeV2File( /** Resolves and serializes one public file attribution. */ export async function toV2File(record: WorkspaceFileRecord): Promise { - const [file] = await toV2Files([record]) - return file + const emailByUserId = await getUserEmailsByIds([record.uploadedBy]) + return serializeV2File( + record, + requireResolvedUserEmail(emailByUserId, record.uploadedBy), + getBaseUrl() + ) } /** Resolves a file page's attribution in one query before serialization. */ export async function toV2Files(records: WorkspaceFileRecord[]): Promise { - const emailByUserId = await findUserEmailsByIds( - records.flatMap((record) => (record.uploadedBy ? [record.uploadedBy] : [])) - ) + const emailByUserId = await getUserEmailsByIds(records.map((record) => record.uploadedBy)) const baseUrl = getBaseUrl() return records.map((record) => - serializeV2File( - record, - record.uploadedBy ? (emailByUserId.get(record.uploadedBy) ?? null) : null, - baseUrl - ) + serializeV2File(record, requireResolvedUserEmail(emailByUserId, record.uploadedBy), baseUrl) ) } diff --git a/apps/sim/lib/api/contracts/v2/files.ts b/apps/sim/lib/api/contracts/v2/files.ts index 1a91cd87d20..9e8a8003ca5 100644 --- a/apps/sim/lib/api/contracts/v2/files.ts +++ b/apps/sim/lib/api/contracts/v2/files.ts @@ -123,8 +123,7 @@ export const v2FileSchema = z ), uploadedByEmail: z .email({ pattern: z.regexes.html5Email }) - .nullable() - .describe('Current email address of the creator, or null after account deletion.') + .describe('Current email address of the uploader.') .meta({ examples: ['jane@example.com'] }), /** ISO-8601 timestamp. */ uploadedAt: z diff --git a/apps/sim/lib/api/contracts/workspace-file-folders.ts b/apps/sim/lib/api/contracts/workspace-file-folders.ts index 082e422c34a..66620f36363 100644 --- a/apps/sim/lib/api/contracts/workspace-file-folders.ts +++ b/apps/sim/lib/api/contracts/workspace-file-folders.ts @@ -19,7 +19,7 @@ export const listWorkspaceFileFoldersQuerySchema = z.object({ export const workspaceFileFolderSchema = z.object({ id: z.string(), workspaceId: z.string(), - userId: z.string().nullable(), + userId: z.string(), name: z.string(), parentId: z.string().nullable(), path: z.string(), diff --git a/apps/sim/lib/api/contracts/workspace-files.ts b/apps/sim/lib/api/contracts/workspace-files.ts index ab13275bb43..22453430e7e 100644 --- a/apps/sim/lib/api/contracts/workspace-files.ts +++ b/apps/sim/lib/api/contracts/workspace-files.ts @@ -152,7 +152,7 @@ export const workspaceFileRecordSchema = z.object({ /** Intrinsic image dimensions (px), populated lazily; null for non-images/un-backfilled rows. */ width: z.number().int().positive().nullable().optional(), height: z.number().int().positive().nullable().optional(), - uploadedBy: z.string().nullable(), + uploadedBy: z.string(), folderId: z.string().nullable(), folderPath: z.string().nullable().optional(), deletedAt: z.coerce.date().nullable().optional(), diff --git a/apps/sim/lib/billing/organizations/membership-external-removal.test.ts b/apps/sim/lib/billing/organizations/membership-external-removal.test.ts index a0484192798..4de4e2cadea 100644 --- a/apps/sim/lib/billing/organizations/membership-external-removal.test.ts +++ b/apps/sim/lib/billing/organizations/membership-external-removal.test.ts @@ -1,4 +1,4 @@ -import { credential, knowledgeBase, member, workspaceFiles } from '@sim/db/schema' +import { credential, knowledgeBase, member, user, workspaceFiles } from '@sim/db/schema' import { dbChainMockFns, hasMockCondition, queueTableRows, resetDbChainMock } from '@sim/testing' import { organizationMemberLimitsMock, @@ -39,6 +39,9 @@ describe('external organization access removal', () => { it('preserves organization indexes and cached documents when a creator leaves with no workspaces', async () => { queueTableRows(member, [{ id: 'membership', userId: 'departing', role: 'admin' }]) queueTableRows(member, [{ userId: 'surviving-owner' }]) + queueTableRows(user, [{ email: 'departing@example.test' }]) + queueTableRows(user, [{ email: 'departing@example.test' }]) + queueTableRows(user, [{ id: 'surviving-owner' }]) dbChainMockFns.returning.mockResolvedValueOnce([{ id: 'membership' }]) const result = await removeUserFromOrganization({ @@ -46,6 +49,7 @@ describe('external organization access removal', () => { organizationId: 'organization-a', memberId: 'membership', skipBillingLogic: true, + onError: 'throw', }) expect(result.success).toBe(true) diff --git a/apps/sim/lib/billing/organizations/membership.ts b/apps/sim/lib/billing/organizations/membership.ts index e7b25750441..1232195ef50 100644 --- a/apps/sim/lib/billing/organizations/membership.ts +++ b/apps/sim/lib/billing/organizations/membership.ts @@ -10,7 +10,6 @@ import { account, credential, invitation, - knowledgeBase, member, organization, permissionGroupMember, @@ -19,7 +18,6 @@ import { user, userStats, workspace, - workspaceFiles, } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' @@ -38,6 +36,7 @@ import { } from '@/lib/billing/enterprise-outbox' import { acquireUserBillingIdentityLock } from '@/lib/billing/organizations/billing-identity-lock' import { setOrgMemberUsageLimit } from '@/lib/billing/organizations/member-limits' +import { reassignOrganizationSharedResourcesTx } from '@/lib/billing/organizations/resource-handoff' import { isPaid, sqlIsPro } from '@/lib/billing/plan-helpers' import { changeOrganizationWorkspaceBilledAccountsInTx } from '@/lib/billing/storage/payer-transfer' import { @@ -61,11 +60,10 @@ import { revokeUserSessionsTx, } from '@/lib/organizations/members/revocation' import { reassignOrganizationProjects } from '@/lib/projects/membership' +import { handoffProjectsForOrganizationDepartureTx } from '@/lib/projects/resource-handoff' import { removeWorkspaceSkillMembershipsTx } from '@/lib/skills/access' -import { - reassignWorkflowOwnershipForWorkspaceMemberRemovalTx, - WorkspaceBillingAccountRemovalError, -} from '@/lib/workspaces/utils' +import { reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx } from '@/lib/workspaces/resource-handoff' +import { WorkspaceBillingAccountRemovalError } from '@/lib/workspaces/utils' import { endDirectoryMembershipTx } from '@/ee/scim/lib/identity/end-directory-membership' export { acquireUserBillingIdentityLock } from '@/lib/billing/organizations/billing-identity-lock' @@ -547,14 +545,8 @@ async function reassignOwnedOrganizationResourcesTx({ organizationId: string workspaceIds: string[] }) { - const [ownerMembership] = await tx - .select({ userId: member.userId }) - .from(member) - .where(and(eq(member.organizationId, organizationId), eq(member.role, 'owner'))) - .limit(1) - - const ownerId = ownerMembership?.userId - if (!ownerId || ownerId === userId) return 0 + const ownerId = await reassignOrganizationSharedResourcesTx(tx, organizationId, userId) + if (!ownerId) return 0 await reassignOrganizationProjects(tx, { organizationId, @@ -562,28 +554,7 @@ async function reassignOwnedOrganizationResourcesTx({ toUserId: ownerId, workspaceIds, }) - - /** Creator attribution must survive account deletion without changing document ACLs. */ - await tx - .update(knowledgeBase) - .set({ userId: ownerId, updatedAt: new Date() }) - .where( - and( - eq(knowledgeBase.organizationId, organizationId), - isNull(knowledgeBase.workspaceId), - eq(knowledgeBase.userId, userId) - ) - ) - await tx - .update(workspaceFiles) - .set({ userId: ownerId, updatedAt: new Date() }) - .where( - and( - eq(workspaceFiles.organizationId, organizationId), - isNull(workspaceFiles.workspaceId), - eq(workspaceFiles.userId, userId) - ) - ) + await handoffProjectsForOrganizationDepartureTx(tx, organizationId, userId) if (workspaceIds.length === 0) return 0 @@ -1206,13 +1177,13 @@ export async function transferUserBetweenOrganizations( workspaceIds, }) if (workspaceIds.length > 0) { - const workflowOwnershipReassignment = - await reassignWorkflowOwnershipForWorkspaceMemberRemovalTx({ + const resourceOwnershipReassignment = + await reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx({ tx, workspaceIds, departingUserId: params.userId, }) - if (workflowOwnershipReassignment.unresolved.length > 0) { + if (resourceOwnershipReassignment.unresolved.length > 0) { throw new WorkspaceBillingAccountRemovalError() } const deletedPermissions = await tx @@ -1435,13 +1406,13 @@ export async function removeUserFromOrganization( } } - const workflowOwnershipReassignment = - await reassignWorkflowOwnershipForWorkspaceMemberRemovalTx({ + const resourceOwnershipReassignment = + await reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx({ tx, workspaceIds, departingUserId: userId, }) - if (workflowOwnershipReassignment.unresolved.length > 0) { + if (resourceOwnershipReassignment.unresolved.length > 0) { throw new WorkspaceBillingAccountRemovalError() } @@ -1652,13 +1623,13 @@ export async function removeExternalUserFromOrganizationWorkspaces(params: { } } - const workflowOwnershipReassignment = - await reassignWorkflowOwnershipForWorkspaceMemberRemovalTx({ + const resourceOwnershipReassignment = + await reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx({ tx, workspaceIds, departingUserId: userId, }) - if (workflowOwnershipReassignment.unresolved.length > 0) { + if (resourceOwnershipReassignment.unresolved.length > 0) { throw new WorkspaceBillingAccountRemovalError() } diff --git a/apps/sim/lib/billing/organizations/resource-handoff.ts b/apps/sim/lib/billing/organizations/resource-handoff.ts new file mode 100644 index 00000000000..21c01b3db15 --- /dev/null +++ b/apps/sim/lib/billing/organizations/resource-handoff.ts @@ -0,0 +1,107 @@ +import { + knowledgeBase, + member, + permissionGroup, + ssoProvider, + user, + workspaceFiles, +} from '@sim/db/schema' +import { and, asc, eq, isNull, ne, sql } from 'drizzle-orm' +import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' + +/** Organization references survive even when their creator no longer has a membership row. */ +export async function listSharedResourceOrganizationIdsForUser(executor: DbOrTx, userId: string) { + const rows = await executor.execute<{ organizationId: string }>(sql` + SELECT DISTINCT "organizationId" FROM ( + SELECT ${knowledgeBase.organizationId} AS "organizationId" FROM ${knowledgeBase} + WHERE ${knowledgeBase.userId} = ${userId} AND ${knowledgeBase.workspaceId} IS NULL + UNION ALL SELECT ${workspaceFiles.organizationId} FROM ${workspaceFiles} + WHERE ${workspaceFiles.userId} = ${userId} AND ${workspaceFiles.workspaceId} IS NULL + UNION ALL SELECT p.organization_id FROM workspace_files f JOIN project p ON p.id = f.project_id WHERE f.user_id = ${userId} + UNION ALL SELECT p.organization_id FROM folder f JOIN project p ON p.id = f.project_id WHERE f.user_id = ${userId} + UNION ALL SELECT ${permissionGroup.organizationId} FROM ${permissionGroup} WHERE ${permissionGroup.createdBy} = ${userId} + UNION ALL SELECT ${ssoProvider.organizationId} FROM ${ssoProvider} WHERE ${ssoProvider.userId} = ${userId} + ) owned WHERE "organizationId" IS NOT NULL ORDER BY "organizationId" + `) + return rows.map((row) => row.organizationId) +} + +/** Retains organization-owned configuration without rewriting ACLs, credentials, or historical actors. */ +export async function reassignOrganizationSharedResourcesTx( + tx: DbTransaction, + organizationId: string, + departingUserId: string +): Promise { + const [successor] = await tx + .select({ userId: member.userId }) + .from(member) + .where( + and( + eq(member.organizationId, organizationId), + eq(member.role, 'owner'), + ne(member.userId, departingUserId) + ) + ) + .orderBy(asc(member.userId)) + .limit(1) + .for('share') + if (!successor) { + const owned = await listSharedResourceOrganizationIdsForUser(tx, departingUserId) + if (owned.includes(organizationId)) { + throw new OrchestrationError( + 'conflict', + 'Shared organization resources need an active organization owner before this account can leave or be deleted.' + ) + } + return null + } + const [account] = await tx + .select({ id: user.id }) + .from(user) + .where(eq(user.id, successor.userId)) + .for('key share') + if (!account) + throw new OrchestrationError( + 'conflict', + 'The organization owner changed. Nothing was changed; try again.' + ) + const userId = successor.userId + const updatedAt = new Date() + await tx + .update(knowledgeBase) + .set({ userId, updatedAt }) + .where( + and( + eq(knowledgeBase.organizationId, organizationId), + isNull(knowledgeBase.workspaceId), + eq(knowledgeBase.userId, departingUserId) + ) + ) + await tx + .update(workspaceFiles) + .set({ userId, updatedAt }) + .where( + and( + eq(workspaceFiles.organizationId, organizationId), + isNull(workspaceFiles.workspaceId), + eq(workspaceFiles.userId, departingUserId) + ) + ) + await tx + .update(permissionGroup) + .set({ createdBy: userId, updatedAt }) + .where( + and( + eq(permissionGroup.organizationId, organizationId), + eq(permissionGroup.createdBy, departingUserId) + ) + ) + await tx + .update(ssoProvider) + .set({ userId }) + .where( + and(eq(ssoProvider.organizationId, organizationId), eq(ssoProvider.userId, departingUserId)) + ) + return userId +} diff --git a/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts b/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts index 356c0940d72..bb248c8b12b 100644 --- a/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts +++ b/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts @@ -107,7 +107,7 @@ workspaceFileFoldersMockFns.mockBuildWorkspaceFileFolderPathMap.mockImplementati workspaceFileFoldersMockFns.mockNormalizeWorkspaceFileItemName.mockImplementation( (name: string) => name ) -usersQueriesMockFns.mockFindUserEmailsByIds.mockImplementation( +usersQueriesMockFns.mockGetUserEmailsByIds.mockImplementation( async () => new Map([['reader', 'reader@example.test']]) ) usersQueriesMockFns.mockRequireResolvedUserEmail.mockImplementation( diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts index 82f819db4b0..00d919cf1de 100644 --- a/apps/sim/lib/projects/account-deletion.ts +++ b/apps/sim/lib/projects/account-deletion.ts @@ -1,7 +1,6 @@ import { db } from '@sim/db' -import { member, permissions, project, projectWorkspace, workspace } from '@sim/db/schema' -import { ORG_ADMIN_ROLES } from '@sim/platform-authz/workspace' -import { and, asc, eq, inArray, ne, or, sql } from 'drizzle-orm' +import { project, projectWorkspace, workspace } from '@sim/db/schema' +import { asc, eq, inArray, or } from 'drizzle-orm' import type { ProjectStorageOwnerSnapshot } from '@/lib/billing/storage/context' import { type ChangeProjectStoragePayerParams, @@ -14,10 +13,16 @@ import { lockProjects, ProjectConflictError, } from '@/lib/projects/membership' +import { + findProjectSuccessor, + handoffProjectCreatorReferencesTx, + listSharedResourceProjectIdsForUser, +} from '@/lib/projects/resource-handoff' import { planBilledAccountReassignmentsForUser } from '@/lib/workspaces/utils' -/** Two indexed lookups; an `OR` around a membership subquery would scan every Project. */ +/** Resolve creator and environment references before loading their canonical Projects. */ async function loadRelatedProjects(executor: DbOrTx, userId: string, doomedWorkspaceIds: string[]) { + const creatorProjectIds = await listSharedResourceProjectIdsForUser(executor, userId) const doomedMemberships = doomedWorkspaceIds.length ? await executor .select({ projectId: projectWorkspace.projectId }) @@ -30,6 +35,7 @@ async function loadRelatedProjects(executor: DbOrTx, userId: string, doomedWorks .where( or( eq(project.ownerId, userId), + creatorProjectIds.length ? inArray(project.id, creatorProjectIds) : undefined, doomedMemberships.length ? inArray( project.id, @@ -46,65 +52,6 @@ type ProjectDeletionDecision = | { remove: true } | { archive: boolean; ownerId?: string } -/** - * An org admin, else a teammate who administers every surviving environment. With `hold`, - * the successor's membership or grants stay share-locked until commit, so the handoff - * cannot land on someone demoted concurrently. - */ -async function findProjectSuccessor( - executor: DbOrTx, - record: typeof project.$inferSelect, - userId: string, - survivorIds: string[], - hold: boolean -): Promise { - if (record.organizationId) { - const adminQuery = executor - .select({ userId: member.userId }) - .from(member) - .where( - and( - eq(member.organizationId, record.organizationId), - ne(member.userId, userId), - inArray(member.role, ORG_ADMIN_ROLES) - ) - ) - .orderBy(asc(member.userId)) - .limit(1) - const [admin] = await (hold ? adminQuery.for('share') : adminQuery) - if (admin) return admin.userId - } - const [teammate] = await executor - .select({ userId: permissions.userId }) - .from(permissions) - .where( - and( - eq(permissions.entityType, 'workspace'), - eq(permissions.permissionType, 'admin'), - ne(permissions.userId, userId), - inArray(permissions.entityId, survivorIds) - ) - ) - .groupBy(permissions.userId) - .having(sql`count(*) = ${survivorIds.length}`) - .orderBy(asc(permissions.userId)) - .limit(1) - if (!teammate || !hold) return teammate?.userId ?? null - const held = await executor - .select({ id: permissions.id }) - .from(permissions) - .where( - and( - eq(permissions.entityType, 'workspace'), - eq(permissions.permissionType, 'admin'), - eq(permissions.userId, teammate.userId), - inArray(permissions.entityId, survivorIds) - ) - ) - .for('share') - return held.length === survivorIds.length ? teammate.userId : null -} - interface ProjectEnvironment { id: string archivedAt: Date | null @@ -273,6 +220,19 @@ export async function prepareProjectsForAccountDeletion( ) ) } + const creatorProjects = new Set(await listSharedResourceProjectIdsForUser(tx, userId)) + for (const record of records) { + if (!creatorProjects.has(record.id)) continue + if (projectRemovals.some((removed) => removed.projectId === record.id)) continue + const successorId = + projectChanges.find((change) => change.projectId === record.id)?.ownerId ?? record.ownerId + await handoffProjectCreatorReferencesTx( + tx, + { ...record, ownerId: successorId }, + userId, + (environments.get(record.id) ?? []).filter((row) => !doomed.has(row.id)).map((row) => row.id) + ) + } const storageCleanupEventIds: string[] = [] for (const { projectId: id, billableBytes } of removedProjects) { storageCleanupEventIds.push(...(await purgeProjectFilesInTx(tx, id, billableBytes))) diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index 1c12d4c164c..34b5aef8a30 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -404,7 +404,7 @@ export async function reassignOrganizationProjects( * Existing ownership paths can hold workspace rows first; refuse contention instead * of inverting locks. */ -async function tryLockProjects(tx: DbTransaction, projectIds: string[]): Promise { +export async function tryLockProjects(tx: DbTransaction, projectIds: string[]): Promise { const keys = projectIds.map(projectLockKey) if (!(await tryAcquireAdvisoryXactLocks(tx, 'project', keys))) throw new ProjectConflictError('Project is changing; retry the ownership change') diff --git a/apps/sim/lib/projects/resource-handoff.ts b/apps/sim/lib/projects/resource-handoff.ts new file mode 100644 index 00000000000..6c3d586a46b --- /dev/null +++ b/apps/sim/lib/projects/resource-handoff.ts @@ -0,0 +1,277 @@ +import { + folder, + member, + permissions, + project, + projectWorkspace, + user, + workspace, + workspaceFiles, +} from '@sim/db/schema' +import { ORG_ADMIN_ROLES } from '@sim/platform-authz/workspace' +import { and, asc, eq, inArray, ne, sql } from 'drizzle-orm' +import { changeProjectStoragePayersInTx } from '@/lib/billing/storage/payer-transfer' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' +import { + lockProjectBackfillWrites, + lockProjects, + ProjectConflictError, + tryLockProjects, +} from '@/lib/projects/membership' + +/** + * An org admin, else a teammate who administers every surviving environment. With `hold`, + * the successor's membership or grants stay share-locked until commit, so the handoff + * cannot land on someone demoted concurrently. + */ +export async function findProjectSuccessor( + executor: DbOrTx, + record: typeof project.$inferSelect, + userId: string, + survivorIds: string[], + hold: boolean +): Promise { + if (record.organizationId) { + const adminQuery = executor + .select({ userId: member.userId }) + .from(member) + .where( + and( + eq(member.organizationId, record.organizationId), + ne(member.userId, userId), + inArray(member.role, ORG_ADMIN_ROLES) + ) + ) + .orderBy(asc(member.userId)) + .limit(1) + const [admin] = await (hold ? adminQuery.for('share') : adminQuery) + if (admin) return admin.userId + } + const [teammate] = await executor + .select({ userId: permissions.userId }) + .from(permissions) + .where( + and( + eq(permissions.entityType, 'workspace'), + eq(permissions.permissionType, 'admin'), + ne(permissions.userId, userId), + inArray(permissions.entityId, survivorIds) + ) + ) + .groupBy(permissions.userId) + .having(sql`count(*) = ${survivorIds.length}`) + .orderBy(asc(permissions.userId)) + .limit(1) + if (!teammate || !hold) return teammate?.userId ?? null + const held = await executor + .select({ id: permissions.id }) + .from(permissions) + .where( + and( + eq(permissions.entityType, 'workspace'), + eq(permissions.permissionType, 'admin'), + eq(permissions.userId, teammate.userId), + inArray(permissions.entityId, survivorIds) + ) + ) + .for('share') + return held.length === survivorIds.length ? teammate.userId : null +} + +/** Includes archived files and prior departures even when no environment grant remains. */ +export async function listSharedResourceProjectIdsForUser(executor: DbOrTx, userId: string) { + const rows = await executor.execute<{ projectId: string }>(sql` + SELECT DISTINCT project_id AS "projectId" FROM ( + SELECT project_id FROM workspace_files WHERE user_id = ${userId} AND project_id IS NOT NULL + UNION ALL SELECT project_id FROM folder WHERE user_id = ${userId} AND project_id IS NOT NULL + ) owned ORDER BY project_id + `) + return rows.map((row) => row.projectId) +} + +/** Validates the canonical Project owner rather than borrowing an environment's payer. */ +export async function handoffProjectCreatorReferencesTx( + tx: DbTransaction, + record: typeof project.$inferSelect, + departingUserId: string, + environmentIds: string[] +) { + const successorId = record.ownerId + if (successorId === departingUserId) + throw new ProjectConflictError( + 'Project resources need a successor before this account can leave' + ) + const [successor] = await tx + .select({ id: user.id }) + .from(user) + .where(eq(user.id, successorId)) + .for('key share') + if (!successor) throw new ProjectConflictError('Project successor changed; retry the operation') + const [admin] = record.organizationId + ? await tx + .select({ id: member.id }) + .from(member) + .where( + and( + eq(member.organizationId, record.organizationId), + eq(member.userId, successorId), + inArray(member.role, ORG_ADMIN_ROLES) + ) + ) + .for('share') + : [] + if (!admin) { + const grants = environmentIds.length + ? await tx + .select({ id: permissions.entityId }) + .from(permissions) + .where( + and( + eq(permissions.entityType, 'workspace'), + eq(permissions.userId, successorId), + eq(permissions.permissionType, 'admin'), + inArray(permissions.entityId, environmentIds) + ) + ) + .for('share') + : [] + if (!environmentIds.length || grants.length !== environmentIds.length) + throw new ProjectConflictError( + 'Project successor must administer every surviving environment' + ) + } + await tx + .update(workspaceFiles) + .set({ userId: successorId }) + .where(and(eq(workspaceFiles.projectId, record.id), eq(workspaceFiles.userId, departingUserId))) + await tx + .update(folder) + .set({ userId: successorId }) + .where(and(eq(folder.projectId, record.id), eq(folder.userId, departingUserId))) +} + +/** Partial environment departure retains attribution while Project access remains. */ +export async function handoffProjectsForWorkspaceDepartureTx( + tx: DbTransaction, + departingUserId: string, + workspaceIds: string[] +) { + const ids = await lockProjectsForResourceDepartureTx(tx, workspaceIds) + const owned = new Set(await listSharedResourceProjectIdsForUser(tx, departingUserId)) + const affectedIds = ids.filter((id) => owned.has(id)) + if (!affectedIds.length) return + const records = await tx + .select() + .from(project) + .where(inArray(project.id, affectedIds)) + .orderBy(asc(project.id)) + for (const record of records) { + const environments = await tx + .select({ id: workspace.id, archivedAt: workspace.archivedAt }) + .from(projectWorkspace) + .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) + .where(eq(projectWorkspace.projectId, record.id)) + const [admin] = record.organizationId + ? await tx + .select({ id: member.id }) + .from(member) + .where( + and( + eq(member.organizationId, record.organizationId), + eq(member.userId, departingUserId), + inArray(member.role, ORG_ADMIN_ROLES) + ) + ) + .for('share') + : [] + const remainingIds = environments + .filter((row) => !row.archivedAt && !workspaceIds.includes(row.id)) + .map((row) => row.id) + const [grant] = remainingIds.length + ? await tx + .select({ id: permissions.id }) + .from(permissions) + .where( + and( + eq(permissions.entityType, 'workspace'), + eq(permissions.userId, departingUserId), + inArray(permissions.entityId, remainingIds) + ) + ) + .for('share') + : [] + if (admin || grant) continue + const environmentIds = environments.map((row) => row.id) + if (record.ownerId === departingUserId) { + const successorId = await findProjectSuccessor( + tx, + record, + departingUserId, + environmentIds, + true + ) + if (!successorId) + throw new ProjectConflictError( + 'Project resources need an administrator successor before departure' + ) + await changeProjectStoragePayersInTx(tx, [ + { + projectId: record.id, + ownerId: successorId, + organizationId: record.organizationId, + expectedCurrentOwner: { ownerId: record.ownerId, organizationId: record.organizationId }, + }, + ]) + record.ownerId = successorId + } + await handoffProjectCreatorReferencesTx(tx, record, departingUserId, environmentIds) + } +} + +/** Takes Project gates before workspace payer locks; handoff may reacquire them in the same transaction. */ +export async function lockProjectsForResourceDepartureTx( + tx: DbTransaction, + workspaceIds: string[] +) { + await lockProjectBackfillWrites(tx, workspaceIds) + const bindings = await tx + .select({ projectId: projectWorkspace.projectId }) + .from(projectWorkspace) + .where(inArray(projectWorkspace.workspaceId, workspaceIds)) + const ids = [...new Set(bindings.map((row) => row.projectId))] + await lockProjects(tx, ids) + return ids +} + +/** Organization departure includes Project-only references left by older environment removals. */ +export async function handoffProjectsForOrganizationDepartureTx( + tx: DbTransaction, + organizationId: string, + departingUserId: string +) { + const ownedIds = await listSharedResourceProjectIdsForUser(tx, departingUserId) + if (!ownedIds.length) return + const records = await tx + .select() + .from(project) + .where(and(eq(project.organizationId, organizationId), inArray(project.id, ownedIds))) + .orderBy(asc(project.id)) + await tryLockProjects( + tx, + records.map((row) => row.id) + ) + for (const record of records) { + const environments = await tx + .select({ id: projectWorkspace.workspaceId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.projectId, record.id)) + const [current] = await tx.select().from(project).where(eq(project.id, record.id)) + if (!current) throw new ProjectConflictError('Project changed during departure') + await handoffProjectCreatorReferencesTx( + tx, + current, + departingUserId, + environments.map((row) => row.id) + ) + } +} diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts index 6d4db312fdf..0c8946b8b3a 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts @@ -6,6 +6,7 @@ import { db } from '@sim/db' import { folder, permissions, + project, projectWorkspace, user, userStats, @@ -17,12 +18,13 @@ import { createSessionPrincipal } from '@sim/testing/factories/principal.factory import { setUploadDirServer, uploadsSetupMock } from '@sim/testing/mocks/uploads-setup.mock' import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' -import { eq, inArray } from 'drizzle-orm' +import { and, eq, inArray } from 'drizzle-orm' import { afterAll, expect, it, vi } from 'vitest' vi.mock('@/lib/uploads/core/setup.server', () => uploadsSetupMock) import { resolveStorageBillingContext } from '@/lib/billing/storage/context' +import { changeProjectStoragePayersInTx } from '@/lib/billing/storage/payer-transfer' import { createWorkspaceFileFolder, listFileFolders, @@ -46,6 +48,7 @@ setUploadDirServer(storageRoot) const creatorId = generateId() const collaboratorId = generateId() const workspaceId = generateId() +const secondWorkspaceId = generateId() const checks: { name: string; status: string; durationMs: number; error?: string }[] = [] afterAll(async () => { @@ -61,7 +64,7 @@ afterAll(async () => { await db.delete(workspaceFiles).where(eq(workspaceFiles.projectId, binding.projectId)) await db.delete(folder).where(eq(folder.projectId, binding.projectId)) } - await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, inArray(workspace.id, [workspaceId, secondWorkspaceId])) await db.delete(user).where(inArray(user.id, [creatorId, collaboratorId])) } finally { await db.$client.end() @@ -74,7 +77,7 @@ afterAll(async () => { } }) -it('departure revokes access without erasing attribution; account deletion retains shared bytes, folders and history', async () => { +it('partial departure retains Project attribution; successor handoff and prior-departure deletion preserve bytes and actors', async () => { const started = performance.now() const name = 'real departure, account deletion, current collaborator reads and writes' try { @@ -106,6 +109,22 @@ it('departure revokes access without erasing attribution; account deletion retai permissionType: 'admin' as const, })) ) + await insertWorkspaceFixture(db, { + id: secondWorkspaceId, + ownerId: collaboratorId, + billedAccountUserId: collaboratorId, + name: 'Second environment', + forkedFromWorkspaceId: workspaceId, + }) + await db.insert(permissions).values( + [creatorId, collaboratorId].map((userId) => ({ + id: generateId(), + userId, + entityType: 'workspace' as const, + entityId: secondWorkspaceId, + permissionType: 'admin' as const, + })) + ) const createdFolder = await createWorkspaceFileFolder({ workspaceId, userId: creatorId, @@ -153,6 +172,16 @@ it('departure revokes access without erasing attribution; account deletion retai context: 'project', folderId: projectFolderId, }) + await db.transaction((tx) => + changeProjectStoragePayersInTx(tx, [ + { + projectId: binding.projectId, + ownerId: creatorId, + organizationId: null, + expectedCurrentOwner: { ownerId: collaboratorId, organizationId: null }, + }, + ]) + ) const billingBefore = await resolveStorageBillingContext(workspaceId) const creator = createSessionPrincipal({ userId: creatorId }) const collaborator = createSessionPrincipal({ userId: collaboratorId }) @@ -167,7 +196,7 @@ it('departure revokes access without erasing attribution; account deletion retai revokeWorkspaceAccessTx(tx, { workspaceId, userId: creatorId }) ) expect(revoked.revoked).toBe(true) - expect((await getWorkspaceFile(workspaceId, uploaded.id))?.uploadedBy).toBe(creatorId) + expect((await getWorkspaceFile(workspaceId, uploaded.id))?.uploadedBy).toBe(collaboratorId) await expect( readWorkspaceFileMetadata.execute({ principal: creator, @@ -178,26 +207,84 @@ it('departure revokes access without erasing attribution; account deletion retai expect(await verifyFileAccess(before.key, creatorId, undefined, context)).toBe(false) expect(await verifyFileAccess(before.key, collaboratorId, undefined, context)).toBe(true) } + expect( + (await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, projectFileId)))[0].userId + ).toBe(creatorId) + await db + .update(permissions) + .set({ permissionType: 'write' }) + .where(and(eq(permissions.userId, collaboratorId), eq(permissions.entityId, workspaceId))) + await expect( + db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { workspaceId: secondWorkspaceId, userId: creatorId }) + ) + ).rejects.toThrow(/Project.*successor/) + expect( + (await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, projectFileId)))[0].userId + ).toBe(creatorId) + expect( + await db + .select() + .from(permissions) + .where(and(eq(permissions.userId, creatorId), eq(permissions.entityId, secondWorkspaceId))) + ).toHaveLength(1) + await db + .update(permissions) + .set({ permissionType: 'admin' }) + .where(and(eq(permissions.userId, collaboratorId), eq(permissions.entityId, workspaceId))) + expect( + ( + await db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { workspaceId: secondWorkspaceId, userId: creatorId }) + ) + ).revoked + ).toBe(true) + expect( + (await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, projectFileId)))[0].userId + ).toBe(collaboratorId) + expect( + (await db.select().from(project).where(eq(project.id, binding.projectId)))[0].ownerId + ).toBe(collaboratorId) + expect( + (await db.select().from(userStats).where(eq(userStats.userId, creatorId)))[0].storageUsedBytes + ).toBe(0) + await db + .update(workspaceFiles) + .set({ userId: creatorId, deletedAt: new Date() }) + .where(eq(workspaceFiles.id, projectFileId)) + await db.update(folder).set({ userId: creatorId }).where(eq(folder.id, projectFolderId)) + await db + .update(permissions) + .set({ permissionType: 'write' }) + .where(and(eq(permissions.userId, collaboratorId), eq(permissions.entityId, workspaceId))) + await expect(deleteUserAccount(creatorId)).rejects.toThrow(/Project.*successor/) + expect(await db.select({ id: user.id }).from(user).where(eq(user.id, creatorId))).toHaveLength( + 1 + ) + await db + .update(permissions) + .set({ permissionType: 'admin' }) + .where(and(eq(permissions.userId, collaboratorId), eq(permissions.entityId, workspaceId))) const deletion = await deleteUserAccount(creatorId) expect(deletion.blockers).toEqual([]) expect(await db.select({ id: user.id }).from(user).where(eq(user.id, creatorId))).toEqual([]) const retained = await getWorkspaceFile(workspaceId, uploaded.id, { throwOnError: true }) if (!retained) throw new Error('Shared file was deleted with its creator') - expect(retained.uploadedBy).toBeNull() + expect(retained.uploadedBy).toBe(collaboratorId) expect((await fetchWorkspaceFileBuffer(retained, { maxBytes: 1024 })).toString()).toBe( 'current' ) expect( (await listWorkspaceFileFolders(workspaceId)).find((folder) => folder.id === createdFolder.id) ?.userId - ).toBeNull() + ).toBe(collaboratorId) expect( (await listFileFolders({ entityType: 'project', entityId: binding.projectId }))[0].userId - ).toBeNull() + ).toBe(collaboratorId) expect( (await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, projectFileId)))[0].userId - ).toBeNull() - expect((await toV2File(retained)).uploadedByEmail).toBeNull() + ).toBe(collaboratorId) + expect((await toV2File(retained)).uploadedByEmail).toBe(`${collaboratorId}@creator.invalid`) await expect( readWorkspaceFileMetadata.execute({ principal: collaborator, @@ -217,7 +304,7 @@ it('departure revokes access without erasing attribution; account deletion retai undefined, { version: { source: 'api', authorUserId: collaboratorId }, syncLiveDoc: false } ) - expect((await getWorkspaceFile(workspaceId, uploaded.id))?.uploadedBy).toBeNull() + expect((await getWorkspaceFile(workspaceId, uploaded.id))?.uploadedBy).toBe(collaboratorId) expect(await resolveStorageBillingContext(workspaceId)).toEqual(billingBefore) checks.push({ name, status: 'passed', durationMs: performance.now() - started }) } catch (error) { diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts index 61d3f63ccf1..69b1ffcf34e 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts @@ -91,7 +91,7 @@ export class WorkspaceFileItemsNotFoundError extends OrchestrationError { export interface WorkspaceFileFolderRecord { id: string workspaceId: string - userId: string | null + userId: string name: string parentId: string | null path: string @@ -147,7 +147,7 @@ export async function loadWorkspaceFileOperationContext( interface RawWorkspaceFileFolder { id: string workspaceId: string - userId: string | null + userId: string name: string parentId: string | null sortOrder: number 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 0088581f76d..c5134f1dfc3 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -160,7 +160,7 @@ export interface WorkspaceFileRecord { /** Intrinsic image pixel dimensions, populated lazily on first view. Null/absent for non-images. */ width?: number | null height?: number | null - uploadedBy: string | null + uploadedBy: string folderId?: string | null folderPath?: string | null deletedAt?: Date | null diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts index 821d4672110..7750bd2da5b 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts @@ -413,7 +413,7 @@ async function pruneExcessWorkspaceFileVersionsInTx( type WorkspaceFileVersionSubject = Pick< WorkspaceFileRecord, 'id' | 'key' | 'size' | 'type' | 'uploadedAt' | 'updatedAt' | 'contentUpdatedAt' -> & { uploadedBy: string | null } +> & { uploadedBy: string } /** One version of a workspace file as readers see it. */ export interface WorkspaceFileVersionRecord { diff --git a/apps/sim/lib/users/account-deletion.ts b/apps/sim/lib/users/account-deletion.ts index f748152778f..277a7a43679 100644 --- a/apps/sim/lib/users/account-deletion.ts +++ b/apps/sim/lib/users/account-deletion.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { + apiKey, dataDrains, document, knowledgeBase, @@ -9,6 +10,7 @@ import { tableRunDispatches, uploadSession, user, + workflowMcpServer, workspaceFile, workspaceFiles, workspaceFileVersion, @@ -24,8 +26,16 @@ import type { AccountDeletionResource, } from '@/lib/api/contracts/user' import { getHighestPriorityPersonalSubscription } from '@/lib/billing/core/plan' -import { isSoleOwnerOfPaidOrganization } from '@/lib/billing/organizations/membership' +import { + acquireOrganizationMutationLock, + isSoleOwnerOfPaidOrganization, +} from '@/lib/billing/organizations/membership' +import { + listSharedResourceOrganizationIdsForUser, + reassignOrganizationSharedResourcesTx, +} from '@/lib/billing/organizations/resource-handoff' import { OrchestrationError } from '@/lib/core/orchestration/types' +import type { DbOrTx } from '@/lib/db/types' import { getProjectAccountDeletionBlockers, prepareProjectsForAccountDeletion, @@ -37,6 +47,10 @@ import { } from '@/lib/table/rows/executions' import type { StorageContext } from '@/lib/uploads' import { isUsingCloudStorage, StorageService } from '@/lib/uploads' +import { + listSharedResourceWorkspaceIdsForUser, + reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx, +} from '@/lib/workspaces/resource-handoff' import { reassignBilledAccountForUser, reassignOwnedWorkspacesForUser, @@ -209,25 +223,10 @@ export interface AccountDeletionFacts { } /** - * Turns the gathered facts into the full picture of an account deletion: what it - * removes, what it hands off, and every reason it would be refused. - * - * The governing rule is that an account is erased only once it stands alone. - * Nearly every table that points at `user.id` does so with `ON DELETE CASCADE`, - * and those cascades do not distinguish a workflow in the account's own workspace - * from a knowledge base it happened to create inside somebody else's — both would - * go. Rather than chase that blast radius across every creator column (and - * silently lose whichever one is added next), deletion refuses while the account - * is still entangled and names the existing action that untangles it: leave the - * workspace, leave the organization, cancel the plan. Each of those already hands - * the account's content to a surviving member on its own well-tested path. - * - * What remains is provably private, so a workspace falls into exactly one bucket: - * - **delete** — nobody else can reach it, so it is erased with the account. - * - **transfer** — the account only pays for it or is recorded as its owner - * while holding no access to it, so moving that anchor to a real admin - * changes nothing anyone can see. - * - **blocked** — anything else. + * Classifies current memberships and workspace ownership/billing anchors. Shared memberships + * must be left before erasure; private workspaces are deleted, and orphaned anchors transfer + * to a surviving administrator. Execution dependencies are checked separately, and the final + * transaction repairs shared creator references from departures before this policy existed. */ export function classifyAccountDeletion(facts: AccountDeletionFacts): AccountDeletionPlan { const blockers: AccountDeletionBlocker[] = [] @@ -307,6 +306,55 @@ function formatResourceNames(resources: AccountDeletionResource[]): string { return formatNames(resources.map((resource) => resource.name)) } +/** Public MCP actors and live shared keys cannot acquire a new identity through attribution handoff. */ +async function sharedExecutionDeletionBlockers( + executor: DbOrTx, + userId: string, + doomedWorkspaceIds: string[], + hold = false +): Promise { + const doomed = new Set(doomedWorkspaceIds) + const serverQuery = executor + .select({ + id: workflowMcpServer.id, + name: workflowMcpServer.name, + workspaceId: workflowMcpServer.workspaceId, + isPublic: workflowMcpServer.isPublic, + }) + .from(workflowMcpServer) + .where(eq(workflowMcpServer.createdBy, userId)) + const servers = await (hold ? serverQuery.for('update') : serverQuery) + const keyQuery = executor + .select({ + id: apiKey.id, + name: apiKey.name, + workspaceId: apiKey.workspaceId, + expiresAt: apiKey.expiresAt, + }) + .from(apiKey) + .where(and(eq(apiKey.userId, userId), eq(apiKey.type, 'workspace'))) + const keys = await (hold ? keyQuery.for('update') : keyQuery) + const retainedServers = servers.filter((row) => row.isPublic && !doomed.has(row.workspaceId)) + const liveKeys = keys.filter( + (row) => + row.workspaceId && + !doomed.has(row.workspaceId) && + (!row.expiresAt || row.expiresAt > new Date()) + ) + const blockers: AccountDeletionBlocker[] = [] + if (retainedServers.length) + blockers.push({ + code: 'shared_workspace', + message: `Public MCP servers ${formatResourceNames(retainedServers)} still use your account to execute. Ask a workspace admin to delete them or make them private before deleting your account. Restore an archived workspace first if needed.`, + }) + if (liveKeys.length) + blockers.push({ + code: 'shared_workspace', + message: `Workspace API keys ${formatResourceNames(liveKeys)} still depend on your account. Ask a workspace admin to replace and revoke them in Settings before deleting your account.`, + }) + return blockers +} + /** Gathers the facts above and classifies them. */ export async function getAccountDeletionPlan(userId: string): Promise { const [workspaces, organizationNames, paidOrgCheck, personalSubscription, drains] = @@ -344,6 +392,13 @@ export async function getAccountDeletionPlan(userId: string): Promise ({ code: 'project_lifecycle' as const, message })) ) + plan.blockers.push( + ...(await sharedExecutionDeletionBlockers( + db, + userId, + plan.workspacesToDelete.map((row) => row.id) + )) + ) return plan } @@ -677,7 +732,7 @@ async function announceCancelledTableWork( * The ordering inside step 2 is load-bearing too: Postgres evaluates the * `NO ACTION` check on `workspace.billed_account_user_id` *before* the `owner_id` * cascade that would have removed the very same workspace, so a workspace the - * account bills for must be handed over or gone before the `user` row is touched. + * account bills for must be handed over or gone before the `user` row is deleted. * * The plan is recomputed here rather than accepted from the caller: a preview is * a display, never an authorization. @@ -694,6 +749,15 @@ export async function deleteUserAccount(userId: string): Promise { + /** Blocks new user FK references through retention discovery, run cancellation, and deletion. */ + await tx.select({ id: user.id }).from(user).where(eq(user.id, userId)).for('update') + const executionBlockers = await sharedExecutionDeletionBlockers( + tx, + userId, + doomedWorkspaceIds, + true + ) + if (executionBlockers.length) throw new AccountDeletionBlockedError(executionBlockers) await prepareProjectsForAccountDeletion(tx, userId, doomedWorkspaceIds) if (doomedWorkspaceIds.length > 0) { /** @@ -753,28 +817,28 @@ export async function deleteUserAccount(userId: string): Promise 0) { + throw new AccountDeletionBlockedError([ + { + code: 'shared_workspace', + message: + 'Shared resources still reference your account. Ask a workspace admin to assign an active billing account before deleting your account. Nothing was changed.', + }, + ]) + } /** * Cancel every table run this account still governs before the `user` row 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 22e9dbe4ee2..27c8a695108 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 @@ -21,7 +21,7 @@ export const readWorkspaceFileSecretProvenance = defineAuthorizedWorkspaceFileUs resolveActiveWorkspaceFileContext(input), async execute({ input, context }): Promise<{ provenance: WorkspaceFileSecretProvenance - ownerUserId: string | null + ownerUserId: string }> { const file = await getWorkspaceFile(context.workspaceId, context.fileId, { throwOnError: true }) if (!file) throw new OrchestrationError('not_found', 'File not found') diff --git a/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts b/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts new file mode 100644 index 00000000000..3d4886cb399 --- /dev/null +++ b/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts @@ -0,0 +1,937 @@ +import { db } from '@sim/db' +import { + account, + apiKey, + chat, + copilotChats, + document, + environment, + folder, + knowledgeBase, + member, + organization, + permissionGroup, + permissions, + projectWorkspace, + ssoProvider, + user, + userTableDefinitions, + userTableRows, + workflow, + workflowMcpServer, + workspace, + workspaceFile, + workspaceFiles, + workspaceFileVersion, +} from '@sim/db/schema' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' +import { createDeferred } from '@sim/testing/helpers/deferred' +import { generateId } from '@sim/utils/id' +import { and, eq, inArray, sql } from 'drizzle-orm' +import { afterAll, describe, expect, it } from 'vitest' +import { removeUserFromOrganization } from '@/lib/billing/organizations/membership' +import { deleteUserAccount, getAccountDeletionPlan } from '@/lib/users/account-deletion' +import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access' + +readTestDatabaseUrl() + +const userIds: string[] = [] +const workspaceIds: string[] = [] +const organizationIds: string[] = [] +const projectFileIds: string[] = [] + +async function seedResources(archived: boolean, joined = true) { + const ownerId = generateId() + const departingId = generateId() + const workspaceId = generateId() + userIds.push(ownerId, departingId) + workspaceIds.push(workspaceId) + await db.insert(user).values( + [ownerId, departingId].map((id) => ({ + id, + name: 'Lifecycle fixture', + email: `${id}@example.test`, + emailVerified: true, + createdAt: new Date(), + updatedAt: new Date(), + })) + ) + await insertWorkspaceFixture(db, { + id: workspaceId, + name: 'Lifecycle fixture', + ownerId, + billedAccountUserId: ownerId, + archivedAt: archived ? new Date() : null, + }) + await db.insert(permissions).values( + (joined ? [ownerId, departingId] : [ownerId]).map((userId) => ({ + id: generateId(), + userId, + entityType: 'workspace', + entityId: workspaceId, + permissionType: 'admin' as const, + })) + ) + const workflowId = generateId() + const chatId = generateId() + const folderId = generateId() + const tableId = generateId() + const rowId = generateId() + const kbId = generateId() + const documentId = generateId() + const fileId = generateId() + const legacyFileId = generateId() + const versionId = generateId() + const archivedAt = archived ? new Date() : null + await db.insert(workflow).values({ + id: workflowId, + userId: departingId, + workspaceId, + name: 'Retained workflow', + lastSynced: new Date(), + createdAt: new Date(), + updatedAt: new Date(), + archivedAt, + }) + await db.insert(chat).values({ + id: chatId, + workflowId, + userId: departingId, + identifier: chatId, + title: 'Retained deployment', + archivedAt, + }) + await db.insert(folder).values({ + id: folderId, + resourceType: 'file', + name: 'Retained folder', + userId: departingId, + workspaceId, + deletedAt: archivedAt, + }) + await db.insert(userTableDefinitions).values({ + id: tableId, + workspaceId, + name: 'Retained table', + schema: { columns: [] }, + createdBy: departingId, + archivedAt, + }) + await db.insert(userTableRows).values({ + id: rowId, + tableId, + workspaceId, + data: { retained: true }, + createdBy: departingId, + }) + await db.insert(knowledgeBase).values({ + id: kbId, + workspaceId, + name: 'Retained knowledge', + userId: departingId, + deletedAt: archivedAt, + }) + await db.insert(document).values({ + id: documentId, + knowledgeBaseId: kbId, + filename: 'retained.txt', + fileUrl: 'data:text/plain,retained', + fileSize: 8, + mimeType: 'text/plain', + uploadedBy: departingId, + }) + await db.insert(workspaceFiles).values({ + id: fileId, + key: `workspace/${workspaceId}/${fileId}.txt`, + workspaceId, + folderId, + userId: departingId, + context: 'workspace', + originalName: 'retained.txt', + contentType: 'text/plain', + sizeBytes: 8, + deletedAt: archivedAt, + }) + await db.insert(workspaceFileVersion).values({ + id: versionId, + fileId, + workspaceId, + version: 1, + key: `workspace/${workspaceId}/${versionId}.txt`, + sizeBytes: 8, + contentType: 'text/plain', + source: 'upload', + authorUserIds: [departingId], + }) + await db.insert(workspaceFile).values({ + id: legacyFileId, + workspaceId, + uploadedBy: departingId, + name: 'legacy.txt', + key: `workspace/${workspaceId}/${legacyFileId}.txt`, + size: 8, + type: 'text/plain', + deletedAt: archivedAt, + }) + return { + ownerId, + departingId, + workspaceId, + workflowId, + chatId, + folderId, + tableId, + rowId, + kbId, + documentId, + fileId, + legacyFileId, + versionId, + archivedAt, + } +} + +async function assertTransferred(fixture: Awaited>) { + const { ownerId } = fixture + expect + .soft(await db.select().from(workflow).where(eq(workflow.id, fixture.workflowId))) + .toMatchObject([{ userId: ownerId, archivedAt: fixture.archivedAt }]) + expect + .soft(await db.select().from(chat).where(eq(chat.id, fixture.chatId))) + .toMatchObject([{ userId: ownerId, archivedAt: fixture.archivedAt }]) + expect + .soft(await db.select().from(folder).where(eq(folder.id, fixture.folderId))) + .toMatchObject([{ userId: ownerId, deletedAt: fixture.archivedAt }]) + expect + .soft( + await db + .select() + .from(userTableDefinitions) + .where(eq(userTableDefinitions.id, fixture.tableId)) + ) + .toMatchObject([{ createdBy: ownerId, archivedAt: fixture.archivedAt }]) + expect + .soft(await db.select().from(knowledgeBase).where(eq(knowledgeBase.id, fixture.kbId))) + .toMatchObject([{ userId: ownerId, deletedAt: fixture.archivedAt }]) + expect + .soft(await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, fixture.fileId))) + .toMatchObject([ + { userId: ownerId, folderId: fixture.folderId, sizeBytes: 8, deletedAt: fixture.archivedAt }, + ]) + expect + .soft(await db.select().from(workspaceFile).where(eq(workspaceFile.id, fixture.legacyFileId))) + .toMatchObject([{ uploadedBy: ownerId, deletedAt: fixture.archivedAt }]) +} + +async function assertRetained(fixture: Awaited>) { + const { departingId, workspaceId } = fixture + await assertTransferred(fixture) + expect.soft(await db.select().from(user).where(eq(user.id, departingId))).toHaveLength(0) + expect + .soft(await db.select().from(workspace).where(eq(workspace.id, workspaceId))) + .toHaveLength(1) + expect + .soft(await db.select().from(userTableRows).where(eq(userTableRows.id, fixture.rowId))) + .toMatchObject([{ data: { retained: true }, createdBy: null }]) + expect + .soft(await db.select().from(document).where(eq(document.id, fixture.documentId))) + .toMatchObject([{ uploadedBy: null, fileSize: 8 }]) + expect + .soft( + await db + .select() + .from(workspaceFileVersion) + .where(eq(workspaceFileVersion.id, fixture.versionId)) + ) + .toMatchObject([{ authorUserIds: [departingId], version: 1 }]) + expect + .soft( + await db + .select() + .from(permissions) + .where(and(eq(permissions.entityId, workspaceId), eq(permissions.userId, departingId))) + ) + .toHaveLength(0) +} + +afterAll(async () => { + if (projectFileIds.length) + await db.delete(workspaceFiles).where(inArray(workspaceFiles.id, projectFileIds)) + if (workspaceIds.length) await deleteWorkspaceFixture(db, inArray(workspace.id, workspaceIds)) + if (organizationIds.length) + await db.delete(organization).where(inArray(organization.id, organizationIds)) + if (userIds.length) await db.delete(user).where(inArray(user.id, userIds)) +}) + +describe('shared resource retention across workspace departure and account erasure', () => { + it.each([false, true])( + 'retains shared roots and their children after removal (archived=%s)', + async (archived) => { + const fixture = await seedResources(archived) + const result = await db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.departingId, + }) + ) + expect(result.revoked).toBe(true) + await assertTransferred(fixture) + await deleteUserAccount(fixture.departingId) + await assertRetained(fixture) + } + ) + + it('repairs references left by departures before the handoff fix', async () => { + const fixture = await seedResources(true, false) + await deleteUserAccount(fixture.departingId) + await assertRetained(fixture) + }) + + it('retains a shared root committed while account deletion waits for its creator', async () => { + const fixture = await seedResources(false, false) + const tableId = generateId() + const ready = createDeferred() + const release = createDeferred() + const writer = db.transaction(async (tx) => { + await tx.insert(userTableDefinitions).values({ + id: tableId, + workspaceId: fixture.workspaceId, + name: 'Concurrent table', + schema: { columns: [] }, + createdBy: fixture.departingId, + }) + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + ready.resolve(connection.pid) + await release.promise + }) + const writerPid = await ready.promise + const deletion = deleteUserAccount(fixture.departingId) + try { + await expect + .poll( + async () => { + const rows = await db.execute( + sql`SELECT 1 FROM pg_stat_activity WHERE ${writerPid} = ANY(pg_blocking_pids(pid))` + ) + return rows.length + }, + { timeout: 5000 } + ) + .toBeGreaterThan(0) + } finally { + release.resolve() + await writer + await deletion + } + expect( + await db.select().from(userTableDefinitions).where(eq(userTableDefinitions.id, tableId)) + ).toMatchObject([{ createdBy: fixture.ownerId }]) + await assertRetained(fixture) + }) + + it('rejects a late ownership reference after account deletion holds the user barrier', async () => { + const fixture = await seedResources(false, false) + const serverId = generateId() + await db.insert(workflowMcpServer).values({ + id: serverId, + workspaceId: fixture.workspaceId, + createdBy: fixture.departingId, + name: 'Concurrent private endpoint', + isPublic: false, + }) + const ready = createDeferred() + const release = createDeferred() + const blocker = db.transaction(async (tx) => { + await tx + .select() + .from(workflowMcpServer) + .where(eq(workflowMcpServer.id, serverId)) + .for('update') + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + ready.resolve(connection.pid) + await release.promise + }) + const blockerPid = await ready.promise + const deletion = deleteUserAccount(fixture.departingId) + let lateWrite: Promise | undefined + try { + await expect + .poll( + async () => { + const rows = await db.execute( + sql`SELECT 1 FROM pg_stat_activity WHERE ${blockerPid} = ANY(pg_blocking_pids(pid))` + ) + return rows.length + }, + { timeout: 5000 } + ) + .toBeGreaterThan(0) + const tableId = generateId() + lateWrite = db + .insert(userTableDefinitions) + .values({ + id: tableId, + workspaceId: fixture.workspaceId, + name: 'Too late table', + schema: { columns: [] }, + createdBy: fixture.departingId, + }) + .then( + () => ({ inserted: true }), + (error: unknown) => ({ error }) + ) + await expect + .poll( + async () => { + const rows = await db.execute(sql` + SELECT 1 FROM pg_stat_activity blocked + WHERE EXISTS (SELECT 1 FROM pg_stat_activity deleting + WHERE ${blockerPid} = ANY(pg_blocking_pids(deleting.pid)) + AND deleting.pid = ANY(pg_blocking_pids(blocked.pid))) + `) + return rows.length + }, + { timeout: 5000 } + ) + .toBeGreaterThan(0) + } finally { + release.resolve() + await blocker + await deletion + } + expect(await lateWrite).toMatchObject({ + error: expect.objectContaining({ cause: expect.objectContaining({ code: '23503' }) }), + }) + await assertRetained(fixture) + }) + + it.each(['payer', 'grant'] as const)( + 'revalidates a concurrent successor %s change before revoking access', + async (kind) => { + const fixture = await seedResources(false) + const ready = createDeferred() + const release = createDeferred() + const change = db.transaction(async (tx) => { + if (kind === 'payer') { + await tx + .update(workspace) + .set({ billedAccountUserId: fixture.departingId }) + .where(eq(workspace.id, fixture.workspaceId)) + } else { + await tx + .delete(permissions) + .where( + and( + eq(permissions.entityId, fixture.workspaceId), + eq(permissions.userId, fixture.ownerId) + ) + ) + } + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + ready.resolve(connection.pid) + await release.promise + }) + const changerPid = await ready.promise + const removal = db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.departingId, + }) + ) + try { + await expect + .poll( + async () => { + const rows = await db.execute( + sql`SELECT 1 FROM pg_stat_activity WHERE ${changerPid} = ANY(pg_blocking_pids(pid))` + ) + return rows.length + }, + { timeout: 5000 } + ) + .toBeGreaterThan(0) + } finally { + release.resolve() + await change + } + expect((await removal).revoked).toBe(false) + expect( + await db + .select() + .from(userTableDefinitions) + .where(eq(userTableDefinitions.id, fixture.tableId)) + ).toMatchObject([{ createdBy: fixture.departingId }]) + expect( + await db + .select() + .from(permissions) + .where( + and( + eq(permissions.entityId, fixture.workspaceId), + eq(permissions.userId, fixture.departingId) + ) + ) + ).toHaveLength(1) + } + ) + + it('keeps private attachments and personal credentials private during departure and erases them with the user', async () => { + const fixture = await seedResources(false) + const privateChatId = generateId() + const privateFileId = generateId() + const credentialId = generateId() + await db + .insert(copilotChats) + .values({ id: privateChatId, userId: fixture.departingId, workspaceId: fixture.workspaceId }) + await db.insert(workspaceFiles).values({ + id: privateFileId, + key: `mothership/${privateFileId}`, + userId: fixture.departingId, + workspaceId: fixture.workspaceId, + chatId: privateChatId, + context: 'mothership', + originalName: 'private.txt', + contentType: 'text/plain', + sizeBytes: 8, + }) + await db.insert(account).values({ + id: credentialId, + accountId: credentialId, + providerId: 'fixture', + userId: fixture.departingId, + accessToken: 'synthetic-private-token', + createdAt: new Date(), + updatedAt: new Date(), + }) + await db.insert(environment).values({ + id: fixture.departingId, + userId: fixture.departingId, + variables: { PRIVATE: 'synthetic' }, + }) + await db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { workspaceId: fixture.workspaceId, userId: fixture.departingId }) + ) + expect( + await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, privateFileId)) + ).toMatchObject([{ userId: fixture.departingId, chatId: privateChatId }]) + expect(await db.select().from(account).where(eq(account.id, credentialId))).toMatchObject([ + { userId: fixture.departingId, accessToken: 'synthetic-private-token' }, + ]) + await deleteUserAccount(fixture.departingId) + expect( + await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, privateFileId)) + ).toHaveLength(0) + expect( + await db.select().from(copilotChats).where(eq(copilotChats.id, privateChatId)) + ).toHaveLength(0) + expect(await db.select().from(account).where(eq(account.id, credentialId))).toHaveLength(0) + expect( + await db.select().from(environment).where(eq(environment.id, fixture.departingId)) + ).toHaveLength(0) + await assertRetained(fixture) + }) + + it('refuses organization-only retention without an owner successor', async () => { + const fixture = await seedResources(false, false) + const organizationId = generateId() + organizationIds.push(organizationId) + await db.insert(organization).values({ + id: organizationId, + name: 'No successor', + slug: organizationId, + createdAt: new Date(), + }) + const kbId = generateId() + await db.insert(knowledgeBase).values({ + id: kbId, + name: 'Stranded knowledge', + userId: fixture.departingId, + organizationId, + isSearchIndex: true, + }) + await expect(deleteUserAccount(fixture.departingId)).rejects.toThrow( + 'active organization owner' + ) + expect(await db.select().from(user).where(eq(user.id, fixture.departingId))).toHaveLength(1) + expect(await db.select().from(knowledgeBase).where(eq(knowledgeBase.id, kbId))).toMatchObject([ + { userId: fixture.departingId }, + ]) + }) + + it('refuses an inaccessible successor without changing ownership or access', async () => { + const fixture = await seedResources(false) + await db + .delete(permissions) + .where( + and(eq(permissions.entityId, fixture.workspaceId), eq(permissions.userId, fixture.ownerId)) + ) + const result = await db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.departingId, + }) + ) + expect(result.revoked).toBe(false) + expect( + await db + .select() + .from(userTableDefinitions) + .where(eq(userTableDefinitions.id, fixture.tableId)) + ).toMatchObject([{ createdBy: fixture.departingId }]) + expect( + await db + .select() + .from(permissions) + .where( + and( + eq(permissions.entityId, fixture.workspaceId), + eq(permissions.userId, fixture.departingId) + ) + ) + ).toHaveLength(1) + await db.delete(permissions).where(eq(permissions.userId, fixture.departingId)) + await expect(deleteUserAccount(fixture.departingId)).rejects.toThrow('active billing account') + expect(await db.select().from(user).where(eq(user.id, fixture.departingId))).toHaveLength(1) + }) + + it('rolls back handoff and access revocation together when the transaction fails', async () => { + const fixture = await seedResources(false) + await expect( + db.transaction(async (tx) => { + expect( + ( + await revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.departingId, + }) + ).revoked + ).toBe(true) + await tx.execute(sql`SELECT 1 / 0`) + }) + ).rejects.toThrow() + expect( + await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, fixture.fileId)) + ).toMatchObject([{ userId: fixture.departingId }]) + expect( + await db + .select() + .from(permissions) + .where( + and( + eq(permissions.entityId, fixture.workspaceId), + eq(permissions.userId, fixture.departingId) + ) + ) + ).toHaveLength(1) + }) + + it.each([false, true])( + 'retains public MCP execution identity and blocks erasure (deleted=%s)', + async (deleted) => { + const fixture = await seedResources(false) + const serverId = generateId() + await db.insert(workflowMcpServer).values({ + id: serverId, + workspaceId: fixture.workspaceId, + createdBy: fixture.departingId, + name: 'Retained public endpoint', + isPublic: true, + deletedAt: deleted ? new Date() : null, + }) + await db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.departingId, + }) + ) + expect( + await db.select().from(workflowMcpServer).where(eq(workflowMcpServer.id, serverId)) + ).toMatchObject([{ createdBy: fixture.departingId, isPublic: true }]) + expect((await getAccountDeletionPlan(fixture.departingId)).blockers).toMatchObject([ + { message: expect.stringContaining('Public MCP servers') }, + ]) + await expect(deleteUserAccount(fixture.departingId)).rejects.toThrow('Public MCP servers') + expect(await db.select().from(user).where(eq(user.id, fixture.departingId))).toHaveLength(1) + await db.delete(workflowMcpServer).where(eq(workflowMcpServer.id, serverId)) + await deleteUserAccount(fixture.departingId) + await assertRetained(fixture) + } + ) + + it.each(['public server', 'workspace key'] as const)( + 'rechecks a %s dependency activated after the deletion preview', + async (kind) => { + const fixture = await seedResources(false, false) + const dependencyId = generateId() + if (kind === 'public server') { + await db.insert(workflowMcpServer).values({ + id: dependencyId, + workspaceId: fixture.workspaceId, + createdBy: fixture.departingId, + name: 'Concurrent endpoint', + isPublic: false, + }) + } else { + await db.insert(apiKey).values({ + id: dependencyId, + workspaceId: fixture.workspaceId, + userId: fixture.departingId, + name: 'Concurrent key', + key: generateId(), + type: 'workspace', + expiresAt: new Date(Date.now() - 60_000), + }) + } + const ready = createDeferred() + const activate = createDeferred() + const change = db.transaction(async (tx) => { + await tx.select().from(user).where(eq(user.id, fixture.departingId)).for('update') + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + ready.resolve(connection.pid) + await activate.promise + if (kind === 'public server') { + await tx + .update(workflowMcpServer) + .set({ isPublic: true }) + .where(eq(workflowMcpServer.id, dependencyId)) + } else { + await tx.update(apiKey).set({ expiresAt: null }).where(eq(apiKey.id, dependencyId)) + } + }) + const changerPid = await ready.promise + const deletion = deleteUserAccount(fixture.departingId).then( + () => ({ deleted: true }), + (error: unknown) => ({ error }) + ) + try { + await expect + .poll( + async () => { + const rows = await db.execute( + sql`SELECT 1 FROM pg_stat_activity WHERE ${changerPid} = ANY(pg_blocking_pids(pid))` + ) + return rows.length + }, + { timeout: 5000 } + ) + .toBeGreaterThan(0) + } finally { + activate.resolve() + await change + } + expect(await deletion).toMatchObject({ error: expect.objectContaining({ code: 'conflict' }) }) + expect(await db.select().from(user).where(eq(user.id, fixture.departingId))).toHaveLength(1) + if (kind === 'public server') { + expect( + await db.select().from(workflowMcpServer).where(eq(workflowMcpServer.id, dependencyId)) + ).toMatchObject([{ createdBy: fixture.departingId, isPublic: true }]) + } else { + expect(await db.select().from(apiKey).where(eq(apiKey.id, dependencyId))).toMatchObject([ + { userId: fixture.departingId, expiresAt: null }, + ]) + } + } + ) + + it('transfers private MCP metadata without rewriting public server actors', async () => { + const fixture = await seedResources(false, false) + const serverId = generateId() + await db.insert(workflowMcpServer).values({ + id: serverId, + workspaceId: fixture.workspaceId, + createdBy: fixture.departingId, + name: 'Retained authenticated endpoint', + isPublic: false, + }) + await deleteUserAccount(fixture.departingId) + expect( + await db.select().from(workflowMcpServer).where(eq(workflowMcpServer.id, serverId)) + ).toMatchObject([{ createdBy: fixture.ownerId, isPublic: false }]) + await assertRetained(fixture) + }) + + it.each(['permanent', 'future', 'expired', 'revoked'] as const)( + 'blocks only live workspace keys (%s)', + async (state) => { + const fixture = await seedResources(false, false) + const keyId = generateId() + await db.insert(apiKey).values({ + id: keyId, + userId: fixture.departingId, + createdBy: fixture.departingId, + workspaceId: fixture.workspaceId, + type: 'workspace', + name: 'Shared key', + key: generateId(), + expiresAt: + state === 'permanent' + ? null + : new Date(Date.now() + (state === 'expired' ? -60_000 : 60_000)), + }) + if (state === 'revoked') await db.delete(apiKey).where(eq(apiKey.id, keyId)) + if (state === 'permanent' || state === 'future') { + await expect(deleteUserAccount(fixture.departingId)).rejects.toThrow('Workspace API keys') + expect(await db.select().from(apiKey).where(eq(apiKey.id, keyId))).toMatchObject([ + { userId: fixture.departingId }, + ]) + await db.delete(apiKey).where(eq(apiKey.id, keyId)) + } + await deleteUserAccount(fixture.departingId) + await assertRetained(fixture) + } + ) + + it.each([true, false])( + 'retains organization-only resources with no workspace membership (currentMember=%s)', + async (currentMember) => { + const fixture = await seedResources(false, false) + const organizationId = generateId() + const memberId = generateId() + const kbId = generateId() + const orgFileId = generateId() + const groupId = generateId() + const providerId = generateId() + organizationIds.push(organizationId) + await db.insert(organization).values({ + id: organizationId, + name: 'Shared organization', + slug: organizationId, + createdAt: new Date(), + }) + await db.insert(member).values({ + id: generateId(), + organizationId, + userId: fixture.ownerId, + role: 'owner', + createdAt: new Date(), + }) + if (currentMember) + await db.insert(member).values({ + id: memberId, + organizationId, + userId: fixture.departingId, + role: 'member', + createdAt: new Date(), + }) + await db.insert(knowledgeBase).values({ + id: kbId, + organizationId, + userId: fixture.departingId, + isSearchIndex: true, + name: 'Organization index', + deletedAt: new Date(), + }) + await db.insert(workspaceFiles).values({ + id: orgFileId, + key: `knowledge-base/${organizationId}/${orgFileId}.txt`, + organizationId, + userId: fixture.departingId, + context: 'knowledge-base', + originalName: 'organization-index.txt', + contentType: 'text/plain', + sizeBytes: 8, + deletedAt: new Date(), + }) + + await db.insert(permissionGroup).values({ + id: groupId, + organizationId, + name: 'Retained policy', + createdBy: fixture.departingId, + config: { tools: ['restricted'] }, + }) + await db.insert(ssoProvider).values({ + id: providerId, + providerId, + organizationId, + userId: fixture.departingId, + domain: `${providerId}.example.test`, + issuer: `https://${providerId}.example.test`, + }) + const environmentId = generateId() + workspaceIds.push(environmentId) + await insertWorkspaceFixture(db, { + id: environmentId, + name: 'Organization Project', + ownerId: fixture.ownerId, + billedAccountUserId: fixture.ownerId, + organizationId, + workspaceMode: 'organization', + }) + const [binding] = await db + .select() + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, environmentId)) + const projectFileId = generateId() + projectFileIds.push(projectFileId) + await db.insert(workspaceFiles).values({ + id: projectFileId, + userId: fixture.departingId, + projectId: binding.projectId, + context: 'project', + key: `project/${binding.projectId}/fixture`, + originalName: 'fixture', + contentType: 'text/plain', + sizeBytes: 8, + deletedAt: new Date(), + }) + if (currentMember) + expect( + ( + await removeUserFromOrganization({ + organizationId, + userId: fixture.departingId, + memberId, + skipBillingLogic: true, + onError: 'throw', + }) + ).success + ).toBe(true) + if (currentMember) { + expect( + await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, projectFileId)) + ).toMatchObject([{ userId: fixture.ownerId }]) + expect( + await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, orgFileId)) + ).toMatchObject([ + { userId: fixture.ownerId, organizationId, workspaceId: null, sizeBytes: 8 }, + ]) + expect( + await db.select().from(knowledgeBase).where(eq(knowledgeBase.id, kbId)) + ).toMatchObject([{ userId: fixture.ownerId }]) + expect( + await db.select().from(permissionGroup).where(eq(permissionGroup.id, groupId)) + ).toMatchObject([{ createdBy: fixture.ownerId }]) + expect( + await db.select().from(ssoProvider).where(eq(ssoProvider.id, providerId)) + ).toMatchObject([{ userId: fixture.ownerId }]) + } + await deleteUserAccount(fixture.departingId) + expect( + await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, projectFileId)) + ).toMatchObject([{ userId: fixture.ownerId }]) + expect( + await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, orgFileId)) + ).toMatchObject([ + { + userId: fixture.ownerId, + organizationId, + workspaceId: null, + sizeBytes: 8, + deletedAt: expect.any(Date), + }, + ]) + expect(await db.select().from(knowledgeBase).where(eq(knowledgeBase.id, kbId))).toMatchObject( + [{ userId: fixture.ownerId }] + ) + expect( + await db.select().from(permissionGroup).where(eq(permissionGroup.id, groupId)) + ).toMatchObject([{ createdBy: fixture.ownerId, config: { tools: ['restricted'] } }]) + expect( + await db.select().from(ssoProvider).where(eq(ssoProvider.id, providerId)) + ).toMatchObject([{ userId: fixture.ownerId, providerId, organizationId }]) + await assertRetained(fixture) + } + ) +}) diff --git a/apps/sim/lib/workspaces/access/workspace-access.ts b/apps/sim/lib/workspaces/access/workspace-access.ts index deffbea554a..e9a2ed41053 100644 --- a/apps/sim/lib/workspaces/access/workspace-access.ts +++ b/apps/sim/lib/workspaces/access/workspace-access.ts @@ -1,12 +1,13 @@ -import { permissions } from '@sim/db/schema' +import { permissions, workspace } from '@sim/db/schema' import type { PermissionType } from '@sim/platform-authz/workspace' import { generateId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' import { revokeWorkspaceCredentialMembershipsTx } from '@/lib/credentials/access' -import type { DbOrTx } from '@/lib/db/types' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' +import { lockProjectsForResourceDepartureTx } from '@/lib/projects/resource-handoff' import { removeWorkspaceSkillMembershipsTx } from '@/lib/skills/access' +import { reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx } from '@/lib/workspaces/resource-handoff' import { - reassignWorkflowOwnershipForWorkspaceMemberRemovalTx, transferWorkspaceOwnershipToBilledAccountForMemberRemovalTx, WorkspaceBillingAccountRemovalError, } from '@/lib/workspaces/utils' @@ -117,39 +118,42 @@ export async function lowerWorkspaceAccessTx( export type RevokeWorkspaceAccessResult = /** `ownershipTransferred` is true when the departing user owned the workspace and it moved to the billed account. */ | { revoked: true; ownershipTransferred: boolean } - /** Workflows whose owner could not be reassigned; the access row is left in place. */ + /** Workspace IDs whose shared resources or ownership lack a successor; access remains unchanged. */ | { revoked: false; reason: 'unresolved-workflows'; unresolvedWorkflows: string[] } /** The user owns the workspace and it has no billed account to hand it to. */ | { revoked: false; reason: 'workspace-owner-without-successor' } + /** The requested membership no longer exists; a replacement grant must remain untouched. */ + | { revoked: false; reason: 'membership-changed' } -/** - * Removes a user's access to one workspace and everything that hangs off it. - * - * Ownership moves first, in the same order the members route uses: the workspace - * itself to its billed account when the departing user owns it, then every - * workflow they own to a remaining member. Either can fail, and a failure is a - * refusal rather than a partial removal — deleting the access row would orphan - * what could not be moved. - */ +/** Reassigns shared resources and workspace ownership before revoking access in the same transaction. */ export async function revokeWorkspaceAccessTx( - tx: DbOrTx, - params: { workspaceId: string; userId: string } + tx: DbTransaction, + params: { workspaceId: string; userId: string; expectedPermissionId?: string } ): Promise { - let ownershipTransferred: boolean - try { - ownershipTransferred = await transferWorkspaceOwnershipToBilledAccountForMemberRemovalTx({ - tx, - workspaceId: params.workspaceId, - departingUserId: params.userId, - }) - } catch (error) { - if (error instanceof WorkspaceBillingAccountRemovalError) { - return { revoked: false, reason: 'workspace-owner-without-successor' } - } - throw error + await lockProjectsForResourceDepartureTx(tx, [params.workspaceId]) + if (params.expectedPermissionId !== undefined) { + /** Match the handoff's workspace-before-grant lock order before validating the selected row. */ + await tx + .select({ id: workspace.id }) + .from(workspace) + .where(eq(workspace.id, params.workspaceId)) + .for('no key update') + const [selectedGrant] = await tx + .select({ id: permissions.id }) + .from(permissions) + .where( + and( + eq(permissions.id, params.expectedPermissionId), + eq(permissions.userId, params.userId), + eq(permissions.entityType, 'workspace'), + eq(permissions.entityId, params.workspaceId) + ) + ) + .for('update') + if (!selectedGrant) return { revoked: false, reason: 'membership-changed' } } - const reassignment = await reassignWorkflowOwnershipForWorkspaceMemberRemovalTx({ + const reassignment = await reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx({ tx, workspaceIds: [params.workspaceId], departingUserId: params.userId, @@ -162,13 +166,30 @@ export async function revokeWorkspaceAccessTx( } } + let ownershipTransferred: boolean + try { + ownershipTransferred = await transferWorkspaceOwnershipToBilledAccountForMemberRemovalTx({ + tx, + workspaceId: params.workspaceId, + departingUserId: params.userId, + }) + } catch (error) { + if (error instanceof WorkspaceBillingAccountRemovalError) { + return { revoked: false, reason: 'workspace-owner-without-successor' } + } + throw error + } + await tx .delete(permissions) .where( and( eq(permissions.userId, params.userId), eq(permissions.entityType, 'workspace'), - eq(permissions.entityId, params.workspaceId) + eq(permissions.entityId, params.workspaceId), + params.expectedPermissionId !== undefined + ? eq(permissions.id, params.expectedPermissionId) + : undefined ) ) diff --git a/apps/sim/lib/workspaces/resource-handoff.ts b/apps/sim/lib/workspaces/resource-handoff.ts new file mode 100644 index 00000000000..847fabe3410 --- /dev/null +++ b/apps/sim/lib/workspaces/resource-handoff.ts @@ -0,0 +1,196 @@ +import { + chat, + folder, + knowledgeBase, + member, + permissions, + user, + userTableDefinitions, + workflow, + workflowMcpServer, + workspace, + workspaceFile, + workspaceFiles, +} from '@sim/db/schema' +import { ORG_ADMIN_ROLES } from '@sim/platform-authz/workspace' +import { and, asc, eq, inArray, isNull, sql } from 'drizzle-orm' +import type { DbOrTx, DbTransaction } from '@/lib/db/types' +import { + handoffProjectsForWorkspaceDepartureTx, + lockProjectsForResourceDepartureTx, +} from '@/lib/projects/resource-handoff' +import { reassignWorkflowOwnershipForWorkspaceMemberRemovalTx } from '@/lib/workspaces/utils' + +/** Private chat attachments keep their user's lifecycle even when stored under a workspace key. */ +const SHARED_FILE_CONTEXTS = ['workspace', 'knowledge-base', 'execution', 'chat'] + +/** Finds surviving containers from ownership references, including departures predating handoff. */ +export async function listSharedResourceWorkspaceIdsForUser(executor: DbOrTx, userId: string) { + const rows = await executor.execute<{ workspaceId: string }>(sql` + SELECT DISTINCT "workspaceId" FROM ( + SELECT ${workflow.workspaceId} AS "workspaceId" FROM ${workflow} WHERE ${workflow.userId} = ${userId} + UNION ALL SELECT ${folder.workspaceId} FROM ${folder} WHERE ${folder.userId} = ${userId} + UNION ALL SELECT ${userTableDefinitions.workspaceId} FROM ${userTableDefinitions} WHERE ${userTableDefinitions.createdBy} = ${userId} + UNION ALL SELECT ${knowledgeBase.workspaceId} FROM ${knowledgeBase} WHERE ${knowledgeBase.userId} = ${userId} + UNION ALL SELECT ${workspaceFiles.workspaceId} FROM ${workspaceFiles} + WHERE ${workspaceFiles.userId} = ${userId} AND ${workspaceFiles.context} IN ('workspace', 'knowledge-base', 'execution', 'chat') AND ${workspaceFiles.chatId} IS NULL + UNION ALL SELECT ${workspaceFile.workspaceId} FROM ${workspaceFile} WHERE ${workspaceFile.uploadedBy} = ${userId} + UNION ALL SELECT ${workflowMcpServer.workspaceId} FROM ${workflowMcpServer} + WHERE ${workflowMcpServer.createdBy} = ${userId} AND NOT ${workflowMcpServer.isPublic} + UNION ALL SELECT ${workflow.workspaceId} FROM ${chat} JOIN ${workflow} ON ${workflow.id} = ${chat.workflowId} WHERE ${chat.userId} = ${userId} + ) owned WHERE "workspaceId" IS NOT NULL + ORDER BY "workspaceId" + `) + return rows.map((row) => row.workspaceId) +} + +/** Holds a real successor and their current grant through the handoff transaction. */ +async function holdWorkspaceSuccessor( + tx: DbTransaction, + row: { id: string; organizationId: string | null; billedAccountUserId: string }, + departingUserId: string +): Promise { + const successor = row.billedAccountUserId + if (!successor || successor === departingUserId) return false + const [account] = await tx + .select({ id: user.id }) + .from(user) + .where(eq(user.id, successor)) + .for('key share') + if (!account) return false + const [grant] = await tx + .select({ id: permissions.id }) + .from(permissions) + .where( + and( + eq(permissions.entityType, 'workspace'), + eq(permissions.entityId, row.id), + eq(permissions.userId, successor) + ) + ) + .for('share') + if (grant) return true + if (!row.organizationId) return false + const [membership] = await tx + .select({ id: member.id }) + .from(member) + .where( + and( + eq(member.organizationId, row.organizationId), + eq(member.userId, successor), + inArray(member.role, ORG_ADMIN_ROLES) + ) + ) + .for('share') + return Boolean(membership) +} + +/** + * Moves shared lifecycle references before access is revoked. No active-row filter: archived + * resources and their children must survive the eventual user cascade too. Successor validation + * finishes before any write, so a refusal cannot leave a partially transferred batch. + */ +export async function reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx({ + tx, + workspaceIds, + departingUserId, +}: { + tx: DbTransaction + workspaceIds: string[] + departingUserId: string +}): Promise<{ unresolved: string[] }> { + const ids = [...new Set(workspaceIds)].sort() + if (!ids.length) return { unresolved: [] } + await lockProjectsForResourceDepartureTx(tx, ids) + const rows = await tx + .select({ + id: workspace.id, + ownerId: workspace.ownerId, + organizationId: workspace.organizationId, + billedAccountUserId: workspace.billedAccountUserId, + }) + .from(workspace) + .where(inArray(workspace.id, ids)) + .orderBy(asc(workspace.id)) + .for('no key update') + + const owned = new Set(await listSharedResourceWorkspaceIdsForUser(tx, departingUserId)) + const unresolved: string[] = [] + for (const row of rows) { + if ( + (owned.has(row.id) || row.ownerId === departingUserId) && + !(await holdWorkspaceSuccessor(tx, row, departingUserId)) + ) + unresolved.push(row.id) + } + if (unresolved.length) return { unresolved } + await handoffProjectsForWorkspaceDepartureTx(tx, departingUserId, ids) + + for (const row of rows) { + if (!owned.has(row.id)) continue + const userId = row.billedAccountUserId + const updatedAt = new Date() + await reassignWorkflowOwnershipForWorkspaceMemberRemovalTx({ + tx, + workspaceIds: [row.id], + departingUserId, + }) + await tx + .update(folder) + .set({ userId, updatedAt }) + .where(and(eq(folder.workspaceId, row.id), eq(folder.userId, departingUserId))) + await tx + .update(userTableDefinitions) + .set({ createdBy: userId, updatedAt }) + .where( + and( + eq(userTableDefinitions.workspaceId, row.id), + eq(userTableDefinitions.createdBy, departingUserId) + ) + ) + await tx + .update(knowledgeBase) + .set({ userId, updatedAt }) + .where(and(eq(knowledgeBase.workspaceId, row.id), eq(knowledgeBase.userId, departingUserId))) + await tx + .update(workspaceFiles) + .set({ userId, updatedAt }) + .where( + and( + eq(workspaceFiles.workspaceId, row.id), + eq(workspaceFiles.userId, departingUserId), + inArray(workspaceFiles.context, SHARED_FILE_CONTEXTS), + isNull(workspaceFiles.chatId) + ) + ) + await tx + .update(workspaceFile) + .set({ uploadedBy: userId }) + .where( + and(eq(workspaceFile.workspaceId, row.id), eq(workspaceFile.uploadedBy, departingUserId)) + ) + await tx + .update(chat) + .set({ userId, updatedAt }) + .where( + and( + eq(chat.userId, departingUserId), + inArray( + chat.workflowId, + tx.select({ id: workflow.id }).from(workflow).where(eq(workflow.workspaceId, row.id)) + ) + ) + ) + await tx + .update(workflowMcpServer) + .set({ createdBy: userId, updatedAt }) + .where( + and( + eq(workflowMcpServer.workspaceId, row.id), + eq(workflowMcpServer.createdBy, departingUserId), + eq(workflowMcpServer.isPublic, false) + ) + ) + } + return { unresolved: [] } +} diff --git a/apps/sim/scripts/test-shared-resource-lifecycle-e2e.ts b/apps/sim/scripts/test-shared-resource-lifecycle-e2e.ts new file mode 100644 index 00000000000..3fd859c72bf --- /dev/null +++ b/apps/sim/scripts/test-shared-resource-lifecycle-e2e.ts @@ -0,0 +1,429 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { readFile, writeFile } from 'node:fs/promises' +import { assertDisposableTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { createLogger } from '@sim/logger' +import { sha256Hex } from '@sim/security/hash' +import { getErrorMessage } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { generateId, generateShortId } from '@sim/utils/id' +import { isRecordLike } from '@sim/utils/object' +import { truncate } from '@sim/utils/string' +import { makeSignature } from 'better-auth/crypto' +import postgres from 'postgres' +import { getKnowledgeBaseContract } from '@/lib/api/contracts/knowledge' +import { getTableContract } from '@/lib/api/contracts/tables' +import { readWorkspaceFileContract } from '@/lib/api/contracts/workspace-files' + +/** Real HTTP lifecycle exercise; SQL seeds identities, access fixtures, and a provider-independent document. */ +const logger = createLogger('SharedResourceLifecycleE2E') +function required(name: string): string { + const value = process.env[name] + assert(value, `${name} must be explicitly supplied`) + return value +} +const baseUrl = new URL(required('LIFECYCLE_E2E_BASE_URL')) +assert(['localhost', '127.0.0.1', '[::1]'].includes(baseUrl.hostname)) +assert.equal(baseUrl.protocol, 'http:') +const databaseUrl = assertDisposableTestDatabaseUrl(required('LIFECYCLE_E2E_DATABASE_URL')) +const authSecret = required('LIFECYCLE_E2E_AUTH_SECRET') +const adminKey = required('LIFECYCLE_E2E_ADMIN_KEY') +const reportPath = required('LIFECYCLE_E2E_REPORT_PATH') +const sql = postgres(databaseUrl.toString(), { max: 2 }) +const ownerKey = `sk-sim-fixture-${generateId()}` +const ownerId = generateId() +const departingId = generateId() +const workspaceId = generateId() +const projectId = generateId() +const cookies = new Map() +const checks: { name: string; status: string; durationMs: number; error?: string }[] = [] +const requests: { method: string; path: string; status: number }[] = [] +const startedAt = new Date().toISOString() +let fileId = '' +let folderId = '' +let tableId = '' +let kbId = '' +const documentId = generateId() + +function record(value: unknown): Record { + assert(isRecordLike(value), 'Expected an object') + return value +} +function id(value: unknown): string { + assert.equal(typeof value, 'string') + return value as string +} +async function request( + userId: string, + path: string, + method = 'GET', + body?: unknown, + expected = 200 +) { + const response = await fetch(new URL(path, baseUrl), { + method, + headers: { + Cookie: `better-auth.session_token=${cookies.get(userId) ?? ''}`, + Origin: baseUrl.origin, + 'Content-Type': 'application/json', + ...(path.startsWith('/api/v1/admin/') ? { 'x-admin-key': adminKey } : {}), + }, + body: body === undefined ? undefined : JSON.stringify(body), + signal: AbortSignal.timeout(120_000), + redirect: 'error', + }) + requests.push({ method, path, status: response.status }) + const text = await response.text() + assert.equal( + response.status, + expected, + `${method} ${path}: ${response.status} ${truncate(text, 500)}` + ) + return text ? record(JSON.parse(text)) : {} +} +async function readVersionBytes(version: number, expected: string) { + const path = `/api/v2/files/${fileId}/versions/${version}/content?workspaceId=${workspaceId}` + const response = await fetch(new URL(path, baseUrl), { + headers: { 'X-API-Key': ownerKey }, + signal: AbortSignal.timeout(120_000), + }) + requests.push({ method: 'GET', path, status: response.status }) + assert.equal(response.status, 200, await response.clone().text()) + assert.equal(await response.text(), expected) +} + +async function check(name: string, run: () => Promise) { + const start = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - start }) + logger.info(name) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - start, + error: getErrorMessage(error), + }) + throw error + } +} + +try { + for (const userId of [ownerId, departingId]) { + const now = new Date() + const token = generateShortId() + await sql`INSERT INTO "user" (id, name, email, normalized_email, email_verified, created_at, updated_at) + VALUES (${userId}, 'Lifecycle E2E', ${`${userId}@example.test`}, ${`${userId}@example.test`}, true, ${now}, ${now})` + await sql`INSERT INTO user_stats (id, user_id) VALUES (${generateId()}, ${userId})` + await sql`INSERT INTO session (id, token, user_id, expires_at, created_at, updated_at) + VALUES (${generateId()}, ${token}, ${userId}, ${new Date(Date.now() + 86_400_000)}, ${now}, ${now})` + cookies.set(userId, encodeURIComponent(`${token}.${await makeSignature(token, authSecret)}`)) + } + await sql`INSERT INTO api_key (id, user_id, name, key, key_hash, type) VALUES (${generateId()}, ${ownerId}, 'Lifecycle fixture', ${ownerKey}, ${sha256Hex(ownerKey)}, 'personal')` + await sql.begin(async (tx) => { + await tx`INSERT INTO project (id,name,owner_id) VALUES (${projectId},'Lifecycle HTTP fixture',${ownerId})` + await tx`INSERT INTO workspace (id, name, owner_id, billed_account_user_id) + VALUES (${workspaceId}, 'Lifecycle HTTP fixture', ${ownerId}, ${ownerId})` + await tx`INSERT INTO project_workspace (project_id,workspace_id) VALUES (${projectId},${workspaceId})` + }) + for (const userId of [ownerId, departingId]) { + await sql`INSERT INTO permissions (id, user_id, entity_type, entity_id, permission_type) + VALUES (${generateId()}, ${userId}, 'workspace', ${workspaceId}, 'admin')` + } + await check('Create shared roots and children through HTTP', async () => { + folderId = id( + record( + ( + await request(departingId, `/api/workspaces/${workspaceId}/files/folders`, 'POST', { + name: 'Retained folder', + }) + ).folder + ).id + ) + tableId = id( + record( + record( + ( + await request(departingId, '/api/table', 'POST', { + workspaceId, + name: 'Retained_table', + schema: { columns: [{ name: 'value', type: 'string' }] }, + }) + ).data + ).table + ).id + ) + await request(departingId, `/api/table/${tableId}/rows`, 'POST', { + workspaceId, + data: { value: 'survives' }, + }) + kbId = id( + record( + ( + await request(departingId, '/api/knowledge', 'POST', { + workspaceId, + name: 'Retained knowledge', + }) + ).data + ).id + ) + await sql`INSERT INTO document (id, knowledge_base_id, filename, file_url, file_size, mime_type, uploaded_by) + VALUES (${documentId}, ${kbId}, 'fixture.txt', 'data:text/plain,survives', 8, 'text/plain', ${departingId})` + fileId = id( + record( + ( + await request( + departingId, + `/api/workspaces/${workspaceId}/files`, + 'POST', + { name: 'retained.txt', folderId, content: 'version one' }, + 201 + ) + ).file + ).id + ) + await request(departingId, `/api/workspaces/${workspaceId}/files/${fileId}/content`, 'PUT', { + content: 'version two', + }) + }) + const beforeStorage = + await sql`SELECT billed_account_user_id, storage_used_bytes FROM workspace WHERE id = ${workspaceId}` + const versionsBefore = + await sql`SELECT id, author_user_ids FROM workspace_file_version WHERE file_id = ${fileId} ORDER BY version` + await check( + 'Both admin removal endpoints reject an owner transfer to a payer without existing access', + async () => { + await sql`UPDATE workspace SET owner_id = ${departingId} WHERE id = ${workspaceId}` + await sql`DELETE FROM permissions WHERE entity_id = ${workspaceId} AND user_id = ${ownerId}` + const [grant] = + await sql`SELECT id FROM permissions WHERE entity_id = ${workspaceId} AND user_id = ${departingId}` + for (const path of [ + `/api/v1/admin/workspaces/${workspaceId}/members?userId=${departingId}`, + `/api/v1/admin/workspaces/${workspaceId}/members/${grant.id}`, + ]) { + await request(ownerId, path, 'DELETE', undefined, 400) + assert.equal( + (await sql`SELECT owner_id FROM workspace WHERE id = ${workspaceId}`)[0].owner_id, + departingId + ) + assert.equal( + ( + await sql`SELECT id FROM permissions WHERE entity_id = ${workspaceId} AND user_id = ${ownerId}` + ).length, + 0 + ) + assert.equal( + (await sql`SELECT user_id FROM workspace_files WHERE id = ${fileId}`)[0].user_id, + departingId + ) + assert.equal((await sql`SELECT id FROM permissions WHERE id = ${grant.id}`).length, 1) + } + await sql`UPDATE workspace SET owner_id = ${ownerId} WHERE id = ${workspaceId}` + await sql`INSERT INTO permissions (id, user_id, entity_type, entity_id, permission_type) VALUES (${generateId()}, ${ownerId}, 'workspace', ${workspaceId}, 'admin')` + } + ) + await check( + 'A stale membership-ID removal preserves a replacement grant and creator references', + async () => { + const [original] = + await sql`SELECT id FROM permissions WHERE entity_id = ${workspaceId} AND user_id = ${departingId}` + const replacementId = generateId() + let pending: Promise<{ error: unknown } | null> | undefined + try { + await sql.begin(async (tx) => { + const [connection] = await tx`SELECT pg_backend_pid() AS pid` + await tx`SELECT id FROM workspace WHERE id = ${workspaceId} FOR NO KEY UPDATE` + pending = request( + ownerId, + `/api/v1/admin/workspaces/${workspaceId}/members/${original.id}`, + 'DELETE', + undefined, + 404 + ).then( + () => null, + (error: unknown) => ({ error }) + ) + const deadline = Date.now() + 10_000 + let waiting = false + while (Date.now() < deadline) { + const blocked = await tx`SELECT pid FROM pg_stat_activity + WHERE datname = current_database() AND ${connection.pid} = ANY(pg_blocking_pids(pid))` + if (blocked.length) { + waiting = true + break + } + await sleep(25) + } + assert(waiting, 'Removal must reach the workspace lock after reading the original grant') + await tx`DELETE FROM permissions WHERE id = ${original.id}` + await tx`INSERT INTO permissions (id, user_id, entity_type, entity_id, permission_type) + VALUES (${replacementId}, ${departingId}, 'workspace', ${workspaceId}, 'admin')` + }) + const outcome = await pending + assert.deepEqual( + { + grants: Array.from( + await sql`SELECT id FROM permissions WHERE entity_id = ${workspaceId} AND user_id = ${departingId}`, + (row) => ({ id: row.id }) + ), + creator: (await sql`SELECT user_id FROM workspace_files WHERE id = ${fileId}`)[0] + .user_id, + }, + { grants: [{ id: replacementId }], creator: departingId } + ) + if (outcome) throw outcome.error + await request(departingId, `/api/workspaces/${workspaceId}/files/${fileId}`) + } finally { + await pending + } + } + ) + await check('Remove member atomically and deny subsequent read and create requests', async () => { + await request(ownerId, `/api/workspaces/members/${departingId}`, 'DELETE', { workspaceId }) + assert.equal( + (await sql`SELECT created_by FROM user_table_definitions WHERE id = ${tableId}`)[0] + .created_by, + ownerId + ) + assert.equal( + (await sql`SELECT user_id FROM knowledge_base WHERE id = ${kbId}`)[0].user_id, + ownerId + ) + assert.equal( + (await sql`SELECT user_id FROM workspace_files WHERE id = ${fileId}`)[0].user_id, + ownerId + ) + for (const path of [ + `/api/table/${tableId}?workspaceId=${workspaceId}`, + `/api/knowledge/${kbId}`, + `/api/workspaces/${workspaceId}/files/${fileId}`, + ]) { + await request(departingId, path, 'GET', undefined, 404) + } + await request( + departingId, + '/api/table', + 'POST', + { workspaceId, name: 'Denied', schema: { columns: [{ name: 'value', type: 'string' }] } }, + 403 + ) + }) + await check('Delete departed account through authenticated HTTP', async () => { + await request(departingId, '/api/users/me/deletion', 'POST', { + confirmEmail: `${departingId}@example.test`, + }) + assert.equal((await sql`SELECT id FROM "user" WHERE id = ${departingId}`).length, 0) + }) + await check('Survivor reads existing non-null contracts and edits all shared roots', async () => { + const table = getTableContract.response.schema.parse( + await request(ownerId, `/api/table/${tableId}?workspaceId=${workspaceId}`) + ) + assert.equal(table.data.table.createdBy, ownerId) + const kb = getKnowledgeBaseContract.response.schema.parse( + await request(ownerId, `/api/knowledge/${kbId}`) + ) + assert.equal(kb.data.userId, ownerId) + const file = readWorkspaceFileContract.response.schema.parse( + await request(ownerId, `/api/workspaces/${workspaceId}/files/${fileId}`) + ) + assert.equal(file.file.uploadedBy, ownerId) + await readVersionBytes(1, 'version one') + await readVersionBytes(2, 'version two') + await request(ownerId, `/api/table/${tableId}`, 'PATCH', { + workspaceId, + name: 'Retained_table_edited', + }) + const rows = await request(ownerId, `/api/table/${tableId}/rows?workspaceId=${workspaceId}`) + const retainedRows = record(rows.data).rows + assert(Array.isArray(retainedRows)) + assert.deepEqual(record(retainedRows[0]).data, { value: 'survives' }) + await request(ownerId, `/api/knowledge/${kbId}`, 'PUT', { name: 'Retained knowledge edited' }) + await request(ownerId, `/api/workspaces/${workspaceId}/files/folders/${folderId}`, 'PATCH', { + name: 'Retained folder edited', + }) + await request(ownerId, `/api/workspaces/${workspaceId}/files/${fileId}`, 'PATCH', { + name: 'retained-edited.txt', + }) + assert.deepEqual( + await sql`SELECT billed_account_user_id, storage_used_bytes FROM workspace WHERE id = ${workspaceId}`, + beforeStorage + ) + assert.deepEqual( + await sql`SELECT id, author_user_ids FROM workspace_file_version WHERE file_id = ${fileId} ORDER BY version`, + versionsBefore + ) + assert.equal((await sql`SELECT id FROM document WHERE id = ${documentId}`).length, 1) + await request(ownerId, `/api/workspaces/${workspaceId}/files/${fileId}/content`, 'PUT', { + content: 'survivor version', + }) + }) +} catch (error) { + logger.error('Lifecycle E2E failed', { error }) + process.exitCode = 1 +} finally { + try { + if (fileId) { + const versions = + await sql`SELECT version FROM workspace_file_version WHERE file_id = ${fileId} ORDER BY version` + for (const version of versions.slice(0, -1)) { + const path = `/api/v2/files/${fileId}/versions/${version.version}?workspaceId=${workspaceId}` + const response = await fetch(new URL(path, baseUrl), { + method: 'DELETE', + headers: { 'X-API-Key': ownerKey }, + signal: AbortSignal.timeout(120_000), + }) + assert.equal(response.status, 200, await response.text()) + } + const [file] = await sql`SELECT key FROM workspace_files WHERE id = ${fileId}` + if (file) + await request(ownerId, '/api/files/delete', 'POST', { + filePath: `/api/files/serve/${file.key}`, + context: 'workspace', + }) + } + } catch (error) { + checks.push({ + name: 'Stored-object cleanup', + status: 'failed', + durationMs: 0, + error: getErrorMessage(error), + }) + process.exitCode = 1 + } finally { + try { + await sql.begin(async (tx) => { + await tx`DELETE FROM workspace WHERE id = ${workspaceId}` + await tx`DELETE FROM project WHERE id = ${projectId}` + }) + await sql`DELETE FROM "user" WHERE id IN (${ownerId}, ${departingId})` + } finally { + await sql.end() + const untrackedHashes: Record = {} + for (const path of execFileSync('git', ['ls-files', '--others', '--exclude-standard'], { + encoding: 'utf8', + }) + .trim() + .split('\n') + .filter(Boolean)) { + untrackedHashes[path] = sha256Hex(await readFile(path)) + } + await writeFile( + reportPath, + JSON.stringify( + { + startedAt, + finishedAt: new Date().toISOString(), + sourceSha: execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(), + trackedDiffSha256: sha256Hex(execFileSync('git', ['diff', 'HEAD'])), + untrackedHashes, + checks, + requests, + }, + null, + 2 + ) + ) + } + } +} diff --git a/packages/db/file-creator-lifetime.integration.ts b/packages/db/file-creator-lifetime.integration.ts index afb66681ecd..56c2a7daa7b 100644 --- a/packages/db/file-creator-lifetime.integration.ts +++ b/packages/db/file-creator-lifetime.integration.ts @@ -2,9 +2,7 @@ import { readFileSync } from 'node:fs' import { mkdir, writeFile } from 'node:fs/promises' import { dirname } from 'node:path' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' -import { createDeferred } from '@sim/testing/helpers/deferred' import { getErrorMessage } from '@sim/utils/errors' -import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import postgres, { type Sql } from 'postgres' import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' @@ -44,16 +42,6 @@ describe('Shared file creator lifetime in PostgreSQL', () => { if (statement.trim()) await sql.unsafe(statement) } } - async function waitForDatabaseLock(pid: number) { - for (let attempt = 0; attempt < 100; attempt += 1) { - const [state] = await sql<{ waiting: boolean }[]>`SELECT EXISTS ( - SELECT 1 FROM pg_stat_activity WHERE pid = ${pid} AND wait_event_type = 'Lock' - ) AS waiting` - if (state.waiting) return - await sleep(10) - } - throw new Error('Creator operation did not wait for its database lock') - } beforeAll(async () => { admin = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) await admin.unsafe(`CREATE SCHEMA "${schemaName}"`) @@ -127,221 +115,54 @@ describe('Shared file creator lifetime in PostgreSQL', () => { }) check( - 'creator deletion preserves Project folders, heads and history with a nullable creator', + 'Project deletion guard refuses cascading creator erasure until the handoff is complete', async () => { - await sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) - VALUES ('folder', 'Docs', 'user-a', 'file', 'project-a')` - await sql`INSERT INTO workspace_files (id, context, user_id, project_id, folder_id) - VALUES ('file', 'project', 'user-a', 'project-a', 'folder')` - await sql`INSERT INTO workspace_file_version (id, file_id, author_user_ids) - VALUES ('version', 'file', ARRAY['user-a'])` - const [before] = - await sql`SELECT key, content_updated_at, secret_provenance_version FROM workspace_files` - await sql`DELETE FROM "user" WHERE id = 'user-a'` - expect(await sql`SELECT user_id, project_id FROM workspace_files`).toEqual([ - { user_id: null, project_id: 'project-a' }, - ]) - expect(await sql`SELECT user_id, project_id FROM folder`).toEqual([ - { user_id: null, project_id: 'project-a' }, + await sql`INSERT INTO folder (id,name,user_id,resource_type,project_id) VALUES ('folder','Docs','user-a','file','project-a')` + await sql`INSERT INTO workspace_files (id,context,user_id,project_id,folder_id) VALUES ('file','project','user-a','project-a','folder')` + await sql`INSERT INTO workspace_file_version (id,file_id,author_user_ids) VALUES ('version','file',ARRAY['user-a'])` + await expect(sql`DELETE FROM "user" WHERE id='user-a'`).rejects.toMatchObject({ + code: '23514', + }) + await sql`UPDATE workspace_files SET user_id='user-b' WHERE id='file'` + await expect(sql`DELETE FROM "user" WHERE id='user-a'`).rejects.toMatchObject({ + code: '23514', + }) + await sql`UPDATE folder SET user_id='user-b' WHERE id='folder'` + await sql`DELETE FROM "user" WHERE id='user-a'` + expect(await sql`SELECT user_id,key FROM workspace_files`).toEqual([ + { user_id: 'user-b', key: 'unchanged-object-key' }, ]) - expect( - ( - await sql`SELECT key, content_updated_at, secret_provenance_version FROM workspace_files` - )[0] - ).toEqual(before) - expect(await sql`SELECT key, author_user_ids FROM workspace_file_version`).toEqual([ - { key: 'unchanged-version-key', author_user_ids: ['user-a'] }, + expect(await sql`SELECT author_user_ids FROM workspace_file_version`).toEqual([ + { author_user_ids: ['user-a'] }, ]) } ) - - check('workspace creator deletion preserves durable files, folders and history', async () => { - await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) - VALUES ('folder', 'Docs', 'user-a', 'workspace-a', 'file')` - await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, folder_id) - VALUES ('file', 'workspace', 'user-a', 'workspace-a', 'folder')` - await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` - await sql`DELETE FROM "user" WHERE id = 'user-a'` - expect(await sql`SELECT id, user_id FROM workspace_files`).toEqual([ - { id: 'file', user_id: null }, - ]) - expect(await sql`SELECT id, user_id FROM folder`).toEqual([{ id: 'folder', user_id: null }]) - expect(await sql`SELECT id FROM workspace_file_version`).toEqual([{ id: 'version' }]) - }) - - check('other file contexts and folder types retain creator cascades', async () => { - for (const context of [ - 'mothership', - 'chat', - 'execution', - 'knowledge-base', - 'workspace-logos', - 'copilot', - 'profile-pictures', - 'general', - ]) { - await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) - VALUES (${context}, ${context}, 'user-a', ${['copilot', 'profile-pictures', 'general'].includes(context) ? null : 'workspace-a'})` - } - await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) - VALUES ('workflow', 'Workflows', 'user-a', 'workspace-a', 'workflow')` - await sql`DELETE FROM "user" WHERE id = 'user-a'` - expect(await sql`SELECT id FROM workspace_files`).toEqual([]) - expect(await sql`SELECT id FROM folder`).toEqual([]) - }) - - check('new resources require a live creator in either scope', async () => { - for (const context of ['workspace', 'project']) { - for (const userId of [null, 'missing-user']) { - await expect(sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, project_id) - VALUES ('missing', ${context}, ${userId}, ${context === 'workspace' ? 'workspace-a' : null}, ${context === 'project' ? 'project-a' : null})`).rejects.toMatchObject( - { code: userId === null ? '23514' : '23503' } - ) - await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, workspace_id, project_id) - VALUES ('missing', 'Docs', ${userId}, 'file', ${context === 'workspace' ? 'workspace-a' : null}, ${context === 'project' ? 'project-a' : null})`).rejects.toMatchObject( - { code: userId === null ? '23514' : '23503' } - ) + check('file creators remain required and actual user references in both scopes', async () => { + for (const context of ['workspace', 'project']) + for (const userId of [null, 'missing']) { + await expect( + sql`INSERT INTO workspace_files (id,context,user_id,workspace_id,project_id) VALUES ('file',${context},${userId},${context === 'workspace' ? 'workspace-a' : null},${context === 'project' ? 'project-a' : null})` + ).rejects.toMatchObject({ code: userId === null ? '23502' : '23503' }) + await expect( + sql`INSERT INTO folder (id,name,user_id,resource_type,workspace_id,project_id) VALUES ('folder','Docs',${userId},'file',${context === 'workspace' ? 'workspace-a' : null},${context === 'project' ? 'project-a' : null})` + ).rejects.toMatchObject({ code: userId === null ? '23502' : '23503' }) } - } }) - - check( - 'surviving workspace content remains editable without claiming creator attribution', - async () => { - await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) - VALUES ('file', 'workspace', 'user-a', 'workspace-a')` - await sql`DELETE FROM "user" WHERE id = 'user-a'` - await sql`UPDATE workspace_files SET key = 'edited-content' WHERE id = 'file'` - expect(await sql`SELECT user_id, key FROM workspace_files`).toEqual([ - { user_id: null, key: 'edited-content' }, - ]) - await sql`DELETE FROM workspace WHERE id = 'workspace-a'` - expect(await sql`SELECT id FROM workspace_files`).toEqual([]) - } - ) - + check('native Project owner references still refuse direct root deletion', async () => { + await sql`INSERT INTO workspace_files (id,context,user_id,project_id) VALUES ('file','project','user-a','project-a')` + await expect(sql`DELETE FROM project WHERE id='project-a'`).rejects.toMatchObject({ + code: '23503', + }) + }) check( - 'retained Project owner deletion stays fail-closed and rolls back creator cleanup', + 'creator guard can be replayed without weakening existing resource protection', async () => { - await sql`UPDATE project SET owner_id = 'user-a' WHERE id = 'project-a'` - await sql`INSERT INTO workspace_files (id, context, user_id, project_id) - VALUES ('file', 'project', 'user-a', 'project-a')` - await expect(sql`DELETE FROM "user" WHERE id = 'user-a'`).rejects.toMatchObject({ - code: '23503', - }) - expect((await sql`SELECT user_id FROM workspace_files`)[0]).toEqual({ user_id: 'user-a' }) - await expect(sql`DELETE FROM project WHERE id = 'project-a'`).rejects.toMatchObject({ - code: '23503', + await applyMigration(migrations[2]) + await applyMigration(migrations[2]) + await sql`INSERT INTO folder (id,name,user_id,resource_type,project_id,deleted_at) VALUES ('folder','Archived','user-a','file','project-a',now())` + await expect(sql`DELETE FROM "user" WHERE id='user-a'`).rejects.toMatchObject({ + code: '23514', }) } ) - - for (const context of ['workspace', 'project'] as const) { - for (const isolation of ['read committed', 'repeatable read'] as const) { - check( - `queued ${isolation} creator deletion preserves a committed concurrent ${context} file or retries`, - async () => { - const snapshot = createDeferred() - const startDelete = createDeferred() - const deletePid = createDeferred() - const inserted = createDeferred() - const releaseInsert = createDeferred() - const deletion = sql.begin(`isolation level ${isolation}`, async (tx) => { - const [connection] = await tx`SELECT pg_backend_pid() AS pid, count(*) FROM "user"` - deletePid.resolve(connection.pid) - snapshot.resolve() - await startDelete.promise - await tx`DELETE FROM "user" WHERE id = 'user-a'` - }) - const deletionOutcome = deletion.then( - () => ({ code: null }), - (error: unknown) => ({ code: (error as { code: string }).code }) - ) - await snapshot.promise - const insertion = sql.begin(async (tx) => { - await tx`INSERT INTO workspace_files (id, context, user_id, workspace_id, project_id) - VALUES ('file', ${context}, 'user-a', ${context === 'workspace' ? 'workspace-a' : null}, ${context === 'project' ? 'project-a' : null})` - inserted.resolve() - await releaseInsert.promise - }) - await Promise.race([insertion, inserted.promise]) - startDelete.resolve() - try { - await waitForDatabaseLock(await deletePid.promise) - } finally { - releaseInsert.resolve() - } - await insertion - const outcome = await deletionOutcome - expect(outcome.code).toBe(isolation === 'repeatable read' ? '40001' : null) - expect((await sql`SELECT user_id FROM workspace_files`)[0]).toEqual({ - user_id: isolation === 'repeatable read' ? 'user-a' : null, - }) - } - ) - } - - check( - `a ${context} insert queued behind creator deletion cannot resurrect a missing creator`, - async () => { - const deleted = createDeferred() - const releaseDelete = createDeferred() - const insertPid = createDeferred() - const deletion = sql.begin(async (tx) => { - await tx`DELETE FROM "user" WHERE id = 'user-a'` - deleted.resolve() - await releaseDelete.promise - }) - await Promise.race([deletion, deleted.promise]) - const insertion = sql.begin(async (tx) => { - const [connection] = await tx`SELECT pg_backend_pid() AS pid` - insertPid.resolve(connection.pid) - await tx`INSERT INTO workspace_files (id, context, user_id, workspace_id, project_id) - VALUES ('file', ${context}, 'user-a', ${context === 'workspace' ? 'workspace-a' : null}, ${context === 'project' ? 'project-a' : null})` - }) - const insertionOutcome = insertion.then( - () => ({ code: null }), - (error: unknown) => ({ code: (error as { code: string }).code }) - ) - try { - await waitForDatabaseLock(await insertPid.promise) - } finally { - releaseDelete.resolve() - } - await deletion - expect((await insertionOutcome).code).toBe('23503') - expect(await sql`SELECT id FROM workspace_files`).toHaveLength(0) - } - ) - check( - `${context} content write racing creator deletion retains the committed content`, - async () => { - await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, project_id) - VALUES ('file', ${context}, 'user-a', ${context === 'workspace' ? 'workspace-a' : null}, ${context === 'project' ? 'project-a' : null})` - const updated = createDeferred() - const releaseUpdate = createDeferred() - const deletePid = createDeferred() - const update = sql.begin(async (tx) => { - await tx`UPDATE workspace_files SET key = 'committed-content' WHERE id = 'file'` - updated.resolve() - await releaseUpdate.promise - }) - await Promise.race([update, updated.promise]) - const deletion = sql.begin(async (tx) => { - const [connection] = await tx`SELECT pg_backend_pid() AS pid` - deletePid.resolve(connection.pid) - await tx`DELETE FROM "user" WHERE id = 'user-a'` - }) - try { - await waitForDatabaseLock(await deletePid.promise) - } finally { - releaseUpdate.resolve() - } - await Promise.all([update, deletion]) - expect(await sql`SELECT user_id, key FROM workspace_files`).toEqual([ - { user_id: null, key: 'committed-content' }, - ]) - } - ) - } }) diff --git a/packages/db/migrations/0400_file_creator_lifetime.sql b/packages/db/migrations/0400_file_creator_lifetime.sql index be70949e373..dcb5c4e841a 100644 --- a/packages/db/migrations/0400_file_creator_lifetime.sql +++ b/packages/db/migrations/0400_file_creator_lifetime.sql @@ -1,49 +1,14 @@ -DROP TRIGGER IF EXISTS project_file_uploader_delete_guard ON "user";--> statement-breakpoint -DROP FUNCTION IF EXISTS project_file_uploader_delete_guard();--> statement-breakpoint -ALTER TABLE "folder" ALTER COLUMN "user_id" DROP NOT NULL;--> statement-breakpoint -ALTER TABLE "workspace_files" ALTER COLUMN "user_id" DROP NOT NULL;--> statement-breakpoint -DO $$ BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_creator_lifetime_check' AND conrelid = 'folder'::regclass) THEN - ALTER TABLE folder ADD CONSTRAINT folder_creator_lifetime_check CHECK ( - user_id IS NOT NULL OR (resource_type = 'file' AND num_nonnulls(workspace_id, project_id) = 1) - ) NOT VALID; - END IF; - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'workspace_files_creator_lifetime_check' AND conrelid = 'workspace_files'::regclass) THEN - ALTER TABLE workspace_files ADD CONSTRAINT workspace_files_creator_lifetime_check CHECK ( - user_id IS NOT NULL OR (context IN ('workspace', 'project') AND num_nonnulls(workspace_id, project_id) = 1 AND organization_id IS NULL) - ) NOT VALID; - END IF; -END $$;--> statement-breakpoint -CREATE OR REPLACE FUNCTION file_require_creator() +CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN - IF NEW.user_id IS NULL THEN - RAISE EXCEPTION 'New file resources require their actual creator' USING ERRCODE = '23514'; + IF EXISTS (SELECT 1 FROM workspace_files WHERE user_id = OLD.id AND project_id IS NOT NULL) + OR EXISTS (SELECT 1 FROM folder WHERE user_id = OLD.id AND project_id IS NOT NULL) THEN + RAISE EXCEPTION 'Project resource creators must be handed to a successor before account deletion' USING ERRCODE = '23514'; END IF; - RETURN NEW; -END; -$$;--> statement-breakpoint -DROP TRIGGER IF EXISTS folder_require_creator ON folder;--> statement-breakpoint -CREATE TRIGGER folder_require_creator -BEFORE INSERT ON folder -FOR EACH ROW EXECUTE FUNCTION file_require_creator();--> statement-breakpoint -DROP TRIGGER IF EXISTS workspace_files_require_creator ON workspace_files;--> statement-breakpoint -CREATE TRIGGER workspace_files_require_creator -BEFORE INSERT ON workspace_files -FOR EACH ROW EXECUTE FUNCTION file_require_creator();--> statement-breakpoint -CREATE OR REPLACE FUNCTION shared_file_creator_delete() -RETURNS trigger LANGUAGE plpgsql AS $$ -BEGIN - UPDATE workspace_files SET user_id = NULL - WHERE user_id = OLD.id AND context IN ('workspace', 'project') - AND num_nonnulls(workspace_id, project_id) = 1 AND organization_id IS NULL; - UPDATE folder SET user_id = NULL - WHERE user_id = OLD.id AND resource_type = 'file' - AND num_nonnulls(workspace_id, project_id) = 1; RETURN OLD; END; $$;--> statement-breakpoint -DROP TRIGGER IF EXISTS shared_file_creator_delete ON "user";--> statement-breakpoint -CREATE TRIGGER shared_file_creator_delete +DROP TRIGGER IF EXISTS project_file_uploader_delete_guard ON "user";--> statement-breakpoint +CREATE TRIGGER project_file_uploader_delete_guard BEFORE DELETE ON "user" -FOR EACH ROW EXECUTE FUNCTION shared_file_creator_delete(); +FOR EACH ROW EXECUTE FUNCTION project_file_uploader_delete_guard(); diff --git a/packages/db/migrations/meta/0400_snapshot.json b/packages/db/migrations/meta/0400_snapshot.json index ff5cd39d301..d1e72cdbc5e 100644 --- a/packages/db/migrations/meta/0400_snapshot.json +++ b/packages/db/migrations/meta/0400_snapshot.json @@ -8551,7 +8551,7 @@ "name": "user_id", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "workspace_id": { "name": "workspace_id", @@ -8839,10 +8839,6 @@ "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "folder_creator_lifetime_check": { - "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR (\"folder\".\"resource_type\" = 'file' AND num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1)" - }, "folder_owner_check": { "name": "folder_owner_check", "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" @@ -28225,7 +28221,7 @@ "name": "user_id", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "workspace_id": { "name": "workspace_id", @@ -28680,10 +28676,6 @@ "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "workspace_files_creator_lifetime_check": { - "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR (\"workspace_files\".\"context\" IN ('workspace', 'project') AND num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\") = 1 AND \"workspace_files\".\"organization_id\" IS NULL)" - }, "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)" diff --git a/packages/db/migrations/meta/0401_snapshot.json b/packages/db/migrations/meta/0401_snapshot.json index 7257df72c3b..6b7862760d8 100644 --- a/packages/db/migrations/meta/0401_snapshot.json +++ b/packages/db/migrations/meta/0401_snapshot.json @@ -8551,7 +8551,7 @@ "name": "user_id", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "workspace_id": { "name": "workspace_id", @@ -8839,10 +8839,6 @@ "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "folder_creator_lifetime_check": { - "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR (\"folder\".\"resource_type\" = 'file' AND num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1)" - }, "folder_owner_check": { "name": "folder_owner_check", "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" @@ -28225,7 +28221,7 @@ "name": "user_id", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "workspace_id": { "name": "workspace_id", @@ -28680,10 +28676,6 @@ "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "workspace_files_creator_lifetime_check": { - "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR (\"workspace_files\".\"context\" IN ('workspace', 'project') AND num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\") = 1 AND \"workspace_files\".\"organization_id\" IS NULL)" - }, "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)" diff --git a/packages/db/migrations/meta/0402_snapshot.json b/packages/db/migrations/meta/0402_snapshot.json index 26f2b8b91a1..593a5f53270 100644 --- a/packages/db/migrations/meta/0402_snapshot.json +++ b/packages/db/migrations/meta/0402_snapshot.json @@ -8551,7 +8551,7 @@ "name": "user_id", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "workspace_id": { "name": "workspace_id", @@ -8839,10 +8839,6 @@ "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "folder_creator_lifetime_check": { - "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR (\"folder\".\"resource_type\" = 'file' AND num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1)" - }, "folder_owner_check": { "name": "folder_owner_check", "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" @@ -28269,7 +28265,7 @@ "name": "user_id", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "workspace_id": { "name": "workspace_id", @@ -28724,10 +28720,6 @@ "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "workspace_files_creator_lifetime_check": { - "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR (\"workspace_files\".\"context\" IN ('workspace', 'project') AND num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\") = 1 AND \"workspace_files\".\"organization_id\" IS NULL)" - }, "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)" diff --git a/packages/db/migrations/meta/0403_snapshot.json b/packages/db/migrations/meta/0403_snapshot.json index c4d3466654d..0bb19d13e21 100644 --- a/packages/db/migrations/meta/0403_snapshot.json +++ b/packages/db/migrations/meta/0403_snapshot.json @@ -8714,7 +8714,7 @@ "name": "user_id", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "workspace_id": { "name": "workspace_id", @@ -9002,10 +9002,6 @@ "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "folder_creator_lifetime_check": { - "name": "folder_creator_lifetime_check", - "value": "\"folder\".\"user_id\" IS NOT NULL OR (\"folder\".\"resource_type\" = 'file' AND num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1)" - }, "folder_owner_check": { "name": "folder_owner_check", "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" @@ -28606,7 +28602,7 @@ "name": "user_id", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "workspace_id": { "name": "workspace_id", @@ -29089,10 +29085,6 @@ "uniqueConstraints": {}, "policies": {}, "checkConstraints": { - "workspace_files_creator_lifetime_check": { - "name": "workspace_files_creator_lifetime_check", - "value": "\"workspace_files\".\"user_id\" IS NOT NULL OR (\"workspace_files\".\"context\" IN ('workspace', 'project') AND num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\") = 1 AND \"workspace_files\".\"organization_id\" IS NULL)" - }, "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)" diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 56d93744e23..0dc2d2aa878 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -235,8 +235,9 @@ export const folder = pgTable( }), resourceType: folderResourceTypeEnum('resource_type').notNull(), name: text('name').notNull(), - /** Durable file folders outlive their creator; other folder types retain their cascade. */ - userId: text('user_id').references(() => user.id, { onDelete: 'cascade' }), + userId: text('user_id') + .notNull() + .references(() => user.id, { onDelete: 'cascade' }), workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'cascade' }), parentId: text('parent_id').references((): AnyPgColumn => folder.id, { onDelete: 'set null', @@ -248,10 +249,6 @@ export const folder = pgTable( deletedAt: timestamp('deleted_at'), }, (table) => ({ - creatorLifetimeCheck: check( - 'folder_creator_lifetime_check', - sql`${table.userId} IS NOT NULL OR (${table.resourceType} = 'file' AND num_nonnulls(${table.workspaceId}, ${table.projectId}) = 1)` - ), ownerCheck: check( 'folder_owner_check', sql`num_nonnulls(${table.workspaceId}, ${table.projectId}) = 1 AND (${table.projectId} IS NULL OR ${table.resourceType} = 'file')` @@ -2496,8 +2493,9 @@ export const workspaceFiles = pgTable( /** Direct owner, independent of creator, caller, and billing payer. */ projectId: text('project_id').references(() => project.id, { onDelete: 'restrict' }), key: text('key').notNull(), - /** Durable shared files outlive their creator; other contexts retain their cascade. */ - userId: text('user_id').references(() => user.id, { onDelete: 'cascade' }), + userId: text('user_id') + .notNull() + .references(() => user.id, { onDelete: 'cascade' }), workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'cascade' }), organizationId: text('organization_id').references(() => organization.id, { onDelete: 'cascade', @@ -2575,10 +2573,6 @@ export const workspaceFiles = pgTable( secretProvenanceVersion: integer('secret_provenance_version'), }, (table) => ({ - creatorLifetimeCheck: check( - 'workspace_files_creator_lifetime_check', - sql`${table.userId} IS NOT NULL OR (${table.context} IN ('workspace', 'project') AND num_nonnulls(${table.workspaceId}, ${table.projectId}) = 1 AND ${table.organizationId} IS NULL)` - ), ownerCheck: check( 'workspace_files_owner_check', sql`num_nonnulls(${table.workspaceId}, ${table.projectId}, ${table.organizationId}) <= 1` diff --git a/packages/sim-cli/src/generated/v2-api.ts b/packages/sim-cli/src/generated/v2-api.ts index 1b2b5144a67..84efd2e93f6 100644 --- a/packages/sim-cli/src/generated/v2-api.ts +++ b/packages/sim-cli/src/generated/v2-api.ts @@ -31,7 +31,7 @@ type AbortFileUploadResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -1522,7 +1522,7 @@ type CompleteFileUploadResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -1833,7 +1833,7 @@ type CreateFileResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -1887,7 +1887,7 @@ type CreateFileUploadResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -4826,7 +4826,7 @@ type EditFileContentResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -5406,7 +5406,7 @@ type GetFileResponseRef1 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -5465,7 +5465,7 @@ type GetFileUploadResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -7879,7 +7879,7 @@ type ListFilesResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -11413,7 +11413,7 @@ type RenameFileResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -11831,7 +11831,7 @@ type RestoreFileResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -12108,7 +12108,7 @@ type RevertFileVersionResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null @@ -12825,7 +12825,7 @@ type UpdateFileContentResponseRef0 = { type: string key: string folderPath: string - uploadedByEmail: string | null + uploadedByEmail: string uploadedAt: string updatedAt: string deletedAt: string | null From 5089d12e2b15201262d3eccc8d965726ecff8d44 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Wed, 7 Oct 2026 20:51:32 -0700 Subject: [PATCH 034/113] fix(files): preserve upload history across creator handoff --- .../[id]/members/[memberId]/route.ts | 6 +- .../v1/admin/workspaces/[id]/members/route.ts | 21 +++- apps/sim/lib/projects/resource-handoff.ts | 10 +- .../file-versions.integration.ts | 105 ++++++++++++++++++ .../contexts/workspace/creator-handoff.ts | 50 +++++++++ .../workspace/workspace-file-versions.ts | 54 ++++++--- .../search/owners.integration.ts | 54 +++++---- .../shared-resource-handoff.integration.ts | 59 ++++++++++ apps/sim/lib/workspaces/resource-handoff.ts | 26 ++--- .../db/public-share-ownership.integration.ts | 5 +- 10 files changed, 325 insertions(+), 65 deletions(-) create mode 100644 apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts index 8cd732d3cc4..00e39fff565 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts @@ -33,6 +33,7 @@ import { } from '@/lib/api/contracts/v1/admin' import { parseRequest } from '@/lib/api/server' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' +import { ProjectConflictError } from '@/lib/projects/membership' import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access' import { getWorkspaceById } from '@/lib/workspaces/permissions/utils' import { WorkspaceBillingAccountRemovalError } from '@/lib/workspaces/utils' @@ -309,7 +310,10 @@ export const DELETE = withRouteHandler( workspaceId, }) } catch (error) { - if (error instanceof WorkspaceBillingAccountRemovalError) { + if ( + error instanceof WorkspaceBillingAccountRemovalError || + error instanceof ProjectConflictError + ) { return badRequestResponse(error.message) } logger.error('Admin API: Failed to remove workspace member', { error, workspaceId, memberId }) diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts index 0f5682492a6..e9ee009ee54 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts @@ -50,6 +50,7 @@ import { import { parseRequest } from '@/lib/api/server' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { syncWorkspaceEnvCredentials } from '@/lib/credentials/environment' +import { ProjectConflictError } from '@/lib/projects/membership' import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access' import { getWorkspaceById } from '@/lib/workspaces/permissions/utils' import { WorkspaceBillingAccountRemovalError } from '@/lib/workspaces/utils' @@ -389,10 +390,19 @@ export const DELETE = withRouteHandler( return notFoundResponse('Workspace member') } - await db.transaction(async (tx) => { - const result = await revokeWorkspaceAccessTx(tx, { workspaceId, userId: userId }) - if (!result.revoked) throw new WorkspaceBillingAccountRemovalError() + const removed = await db.transaction(async (tx) => { + const result = await revokeWorkspaceAccessTx(tx, { + workspaceId, + userId, + expectedPermissionId: existingPermission.id, + }) + if (!result.revoked) { + if (result.reason === 'membership-changed') return false + throw new WorkspaceBillingAccountRemovalError() + } + return true }) + if (!removed) return notFoundResponse('Workspace member') logger.info(`Admin API: Removed user ${userId} from workspace ${workspaceId}`) @@ -409,7 +419,10 @@ export const DELETE = withRouteHandler( return singleResponse({ removed: true, userId, workspaceId }) } catch (error) { - if (error instanceof WorkspaceBillingAccountRemovalError) { + if ( + error instanceof WorkspaceBillingAccountRemovalError || + error instanceof ProjectConflictError + ) { return badRequestResponse(error.message) } logger.error('Admin API: Failed to remove workspace member', { diff --git a/apps/sim/lib/projects/resource-handoff.ts b/apps/sim/lib/projects/resource-handoff.ts index 6c3d586a46b..6c68ed511a5 100644 --- a/apps/sim/lib/projects/resource-handoff.ts +++ b/apps/sim/lib/projects/resource-handoff.ts @@ -18,6 +18,7 @@ import { ProjectConflictError, tryLockProjects, } from '@/lib/projects/membership' +import { handoffFileCreatorsInTx } from '@/lib/uploads/contexts/workspace/creator-handoff' /** * An org admin, else a teammate who administers every surviving environment. With `hold`, @@ -140,10 +141,11 @@ export async function handoffProjectCreatorReferencesTx( 'Project successor must administer every surviving environment' ) } - await tx - .update(workspaceFiles) - .set({ userId: successorId }) - .where(and(eq(workspaceFiles.projectId, record.id), eq(workspaceFiles.userId, departingUserId))) + await handoffFileCreatorsInTx( + tx, + and(eq(workspaceFiles.projectId, record.id), eq(workspaceFiles.userId, departingUserId)), + successorId + ) await tx .update(folder) .set({ userId: successorId }) diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts index 98a075a91e4..03933bc9f8b 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts @@ -32,6 +32,7 @@ import { } from '@/lib/knowledge/__integration__/seed-source-access-fixture' import { createFileReadTransport } from '@/lib/mothership/agent-cli/file-read-transport' import { runCli } from '@/lib/mothership/agent-cli/run-cli' +import { handoffFileCreatorsInTx } from '@/lib/uploads/contexts/workspace/creator-handoff' import { deleteWorkspaceFileVersion, fetchWorkspaceFileBuffer, @@ -133,6 +134,110 @@ describe('workspace file version history in PostgreSQL', () => { expect(await versionRows(fixture.fileId)).toEqual([]) }) + it('keeps original attribution, bytes and revision through repeated creator handoff and a later edit', async () => { + const fixture = await seedFile('original') + const [before] = await db + .select() + .from(workspaceFiles) + .where(eq(workspaceFiles.id, fixture.fileId)) + for (const successor of [fixture.bobId, fixture.aliceId, fixture.bobId]) { + await db.transaction((tx) => + handoffFileCreatorsInTx(tx, eq(workspaceFiles.id, fixture.fileId), successor) + ) + } + const [after] = await db + .select() + .from(workspaceFiles) + .where(eq(workspaceFiles.id, fixture.fileId)) + expect(after).toMatchObject({ + key: before.key, + contentUpdatedAt: before.contentUpdatedAt, + uploadedAt: before.uploadedAt, + sizeBytes: before.sizeBytes, + userId: fixture.bobId, + }) + expect(await versionRows(fixture.fileId)).toHaveLength(1) + await updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.bobId, + Buffer.from('second'), + undefined, + { version: { source: 'api', authorUserId: fixture.bobId } } + ) + const rows = await versionRows(fixture.fileId) + expect(rows.map((row) => [row.version, row.source, row.authorUserIds])).toEqual([ + [1, 'upload', [fixture.aliceId]], + [2, 'api', [fixture.bobId]], + ]) + expect(await readVersionBytes(fixture.workspaceId, fixture.fileId, rows[0].key)).toBe( + 'original' + ) + await db.transaction((tx) => + handoffFileCreatorsInTx(tx, eq(workspaceFiles.id, fixture.fileId), fixture.aliceId) + ) + expect(await versionRows(fixture.fileId)).toEqual(rows) + }) + + it('does not manufacture original authorship for previously overwritten unrecorded content', async () => { + const fixture = await seedFile('overwritten') + await db + .update(workspaceFiles) + .set({ contentUpdatedAt: new Date(Date.now() + 5000) }) + .where(eq(workspaceFiles.id, fixture.fileId)) + await db.transaction((tx) => + handoffFileCreatorsInTx(tx, eq(workspaceFiles.id, fixture.fileId), fixture.bobId) + ) + expect(await versionRows(fixture.fileId)).toMatchObject([ + { version: 1, source: 'unknown', authorUserIds: [] }, + ]) + }) + + it('keeps an empty shell unversioned until its first content write after handoff', async () => { + const fixture = await seedFile('') + await db.transaction((tx) => + handoffFileCreatorsInTx(tx, eq(workspaceFiles.id, fixture.fileId), fixture.bobId) + ) + expect(await versionRows(fixture.fileId)).toEqual([]) + await updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.bobId, + Buffer.from('first'), + undefined, + { version: { source: 'api', authorUserId: fixture.bobId } } + ) + expect(await versionRows(fixture.fileId)).toMatchObject([ + { version: 1, source: 'api', authorUserIds: [fixture.bobId] }, + ]) + }) + + it('serializes content writes with creator handoff without duplicate or misattributed history', async () => { + const fixture = await seedFile('original') + await Promise.all([ + db.transaction((tx) => + handoffFileCreatorsInTx(tx, eq(workspaceFiles.id, fixture.fileId), fixture.bobId) + ), + updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.aliceId, + Buffer.from('second'), + undefined, + { version: { source: 'api', authorUserId: fixture.aliceId } } + ), + ]) + const rows = await versionRows(fixture.fileId) + expect(rows.map((row) => [row.version, row.authorUserIds])).toEqual([ + [1, [fixture.aliceId]], + [2, [fixture.aliceId]], + ]) + expect(rows.filter((row) => row.supersededAt === null)).toHaveLength(1) + expect(await readVersionBytes(fixture.workspaceId, fixture.fileId, rows[0].key)).toBe( + 'original' + ) + }) + it.each([ ['named', 'download'], ['anonymous', 'download'], diff --git a/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts b/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts new file mode 100644 index 00000000000..ab97544c172 --- /dev/null +++ b/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts @@ -0,0 +1,50 @@ +import { workspaceFiles } from '@sim/db/schema' +import { asc, inArray, type SQL } from 'drizzle-orm' +import type { DbTransaction } from '@/lib/db/types' +import { snapshotWorkspaceFileSecretProvenanceInTx } from '@/lib/uploads/contexts/workspace/workspace-file-secret-provenance' +import { + isVersionHeadCurrent, + loadWorkspaceFileVersionHead, + materializeWorkspaceFileVersionInTx, +} from '@/lib/uploads/contexts/workspace/workspace-file-versions' +import { getWorkspaceFileSize } from '@/lib/uploads/shared/types' + +/** Freezes implicit history under the writer lock before replacing a shared file's live creator. */ +export async function handoffFileCreatorsInTx( + tx: DbTransaction, + condition: SQL | undefined, + successorId: string +) { + if (!condition) throw new Error('File creator handoff requires a scope') + const files = await tx + .select() + .from(workspaceFiles) + .where(condition) + .orderBy(asc(workspaceFiles.id)) + .for('update') + for (const file of files) { + if (file.context !== 'workspace' && file.context !== 'project') continue + const head = await loadWorkspaceFileVersionHead(file.id, tx) + if (isVersionHeadCurrent(head, file) || (!head && getWorkspaceFileSize(file) === 0)) continue + const provenance = + file.projectId && file.secretProvenanceVersion === null + ? { status: 'unknown' as const, entries: [] } + : await snapshotWorkspaceFileSecretProvenanceInTx( + tx, + file.id, + file.contentUpdatedAt, + file.secretProvenanceVersion + ) + await materializeWorkspaceFileVersionInTx(tx, file, head, provenance, new Date()) + } + if (!files.length) return + await tx + .update(workspaceFiles) + .set({ userId: successorId, updatedAt: new Date() }) + .where( + inArray( + workspaceFiles.id, + files.map((file) => file.id) + ) + ) +} diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts index 7750bd2da5b..02b5a388838 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts @@ -191,6 +191,35 @@ interface RecordedWorkspaceFileVersion { releasedKeys: string[] } +/** Materializes current bytes under the file lock before content or creator attribution changes. */ +export async function materializeWorkspaceFileVersionInTx( + tx: DbTransaction, + previous: WorkspaceFileRow, + head: WorkspaceFileVersionSummaryRow | undefined, + provenance: WorkspaceFileSecretProvenanceSnapshot, + now: Date +): Promise { + if (isVersionHeadCurrent(head, previous) && head) return head + if (head && head.supersededAt === null) await supersedeVersionInTx(tx, head.id, now) + const original = !head && isOriginalUploadContent(previous) + const [materialized] = await tx + .insert(workspaceFileVersion) + .values({ + id: generateId(), + fileId: previous.id, + workspaceId: previous.workspaceId, + version: head ? head.version + 1 : INITIAL_WORKSPACE_FILE_VERSION, + ...contentColumns(previous, provenance), + contentHash: null, + source: original ? 'upload' : 'unknown', + authorUserIds: original && previous.userId ? [previous.userId] : [], + createdAt: previous.contentUpdatedAt, + updatedAt: previous.contentUpdatedAt, + }) + .returning(versionSummaryColumns) + return materialized +} + /** * Records a committed content write in the file's history. Runs inside the content-write * transaction, under the file row's lock, which serializes version numbering and coalescing @@ -215,24 +244,13 @@ export async function recordWorkspaceFileVersionInTx( if (!params.previousProvenance) { throw new Error('Outgoing workspace file content needs a provenance snapshot to be versioned') } - if (head && head.supersededAt === null) await supersedeVersionInTx(tx, head.id, now) - const original = !head && isOriginalUploadContent(previous) - const [materialized] = await tx - .insert(workspaceFileVersion) - .values({ - id: generateId(), - fileId: previous.id, - workspaceId: params.workspaceId, - version: head ? head.version + 1 : INITIAL_WORKSPACE_FILE_VERSION, - ...contentColumns(previous, params.previousProvenance), - contentHash: null, - source: original ? 'upload' : 'unknown', - authorUserIds: original && previous.userId ? [previous.userId] : [], - createdAt: previous.contentUpdatedAt, - updatedAt: previous.contentUpdatedAt, - }) - .returning(versionSummaryColumns) - head = materialized + head = await materializeWorkspaceFileVersionInTx( + tx, + previous, + head, + params.previousProvenance, + now + ) } const nextColumns = { ...contentColumns(next, params.nextProvenance), updatedAt: now } diff --git a/apps/sim/lib/workspace-files/search/owners.integration.ts b/apps/sim/lib/workspace-files/search/owners.integration.ts index 98502ae0f90..c05c2a3ec15 100644 --- a/apps/sim/lib/workspace-files/search/owners.integration.ts +++ b/apps/sim/lib/workspace-files/search/owners.integration.ts @@ -481,31 +481,37 @@ describe('owner-scoped indexed file search on PostgreSQL and stored bytes', () = } ) - check('keeps empty and recursive folder scopes exact after the creator is detached', async () => { - const f = await fixture() - const folder = await createFolder(f, 'Architecture') - const child = await createFolder(f, 'Backend', folder.folder.id) - const created = await create(f, 'folderneedle', { folderId: child.folder.id }) - await db - .update(workspaceFiles) - .set({ userId: null }) - .where(eq(workspaceFiles.id, created.file.id)) - await indexWorkspaceFileForSearch({ owner: f.owner, ...(await claim(created.file.id)) }, signal) - expect((await search(f, 'folderneedle', { folderPaths: [] })).results).toEqual([]) - expect( - ( - await search(f, 'folderneedle', { - folderPaths: ['/Architecture'], - includeSubfolders: false, - }) - ).results - ).toEqual([]) - expect( - (await search(f, 'folderneedle', { folderPaths: ['/Architecture'] })).results.map( - (hit) => hit.fileId + check( + 'keeps empty and recursive folder scopes exact after the creator is reassigned', + async () => { + const f = await fixture() + const folder = await createFolder(f, 'Architecture') + const child = await createFolder(f, 'Backend', folder.folder.id) + const created = await create(f, 'folderneedle', { folderId: child.folder.id }) + await db + .update(workspaceFiles) + .set({ userId: f.readerId }) + .where(eq(workspaceFiles.id, created.file.id)) + await indexWorkspaceFileForSearch( + { owner: f.owner, ...(await claim(created.file.id)) }, + signal ) - ).toEqual([created.file.id]) - }) + expect((await search(f, 'folderneedle', { folderPaths: [] })).results).toEqual([]) + expect( + ( + await search(f, 'folderneedle', { + folderPaths: ['/Architecture'], + includeSubfolders: false, + }) + ).results + ).toEqual([]) + expect( + (await search(f, 'folderneedle', { folderPaths: ['/Architecture'] })).results.map( + (hit) => hit.fileId + ) + ).toEqual([created.file.id]) + } + ) check('rejects forged owner payloads without changing the current revision', async () => { const f = await fixture() diff --git a/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts b/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts index 3d4886cb399..842eebeafb7 100644 --- a/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts +++ b/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts @@ -282,6 +282,59 @@ describe('shared resource retention across workspace departure and account erasu } ) + it('retains workspace-logo bindings after uploader departure and deletion', async () => { + const fixture = await seedResources(false) + const logoId = generateId() + await db.insert(workspaceFiles).values({ + id: logoId, + userId: fixture.departingId, + workspaceId: fixture.workspaceId, + key: `workspace-logos/${fixture.workspaceId}/${logoId}.png`, + context: 'workspace-logos', + originalName: 'logo.png', + contentType: 'image/png', + sizeBytes: 8, + }) + await db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.departingId, + }) + ) + await deleteUserAccount(fixture.departingId) + expect( + await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, logoId)) + ).toMatchObject([ + { userId: fixture.ownerId, workspaceId: fixture.workspaceId, context: 'workspace-logos' }, + ]) + }) + + it('preserves an implicit original upload when its creator leaves', async () => { + const fixture = await seedResources(false) + await db.delete(workspaceFileVersion).where(eq(workspaceFileVersion.fileId, fixture.fileId)) + await db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.departingId, + }) + ) + expect( + await db + .select() + .from(workspaceFileVersion) + .where(eq(workspaceFileVersion.fileId, fixture.fileId)) + ).toMatchObject([ + { version: 1, source: 'upload', authorUserIds: [fixture.departingId], supersededAt: null }, + ]) + await deleteUserAccount(fixture.departingId) + expect( + await db + .select() + .from(workspaceFileVersion) + .where(eq(workspaceFileVersion.fileId, fixture.fileId)) + ).toMatchObject([{ version: 1, authorUserIds: [fixture.departingId] }]) + }) + it('repairs references left by departures before the handoff fix', async () => { const fixture = await seedResources(true, false) await deleteUserAccount(fixture.departingId) @@ -908,6 +961,12 @@ describe('shared resource retention across workspace departure and account erasu ).toMatchObject([{ userId: fixture.ownerId }]) } await deleteUserAccount(fixture.departingId) + expect( + await db + .select() + .from(workspaceFileVersion) + .where(eq(workspaceFileVersion.fileId, projectFileId)) + ).toMatchObject([{ version: 1, source: 'upload', authorUserIds: [fixture.departingId] }]) expect( await db.select().from(workspaceFiles).where(eq(workspaceFiles.id, projectFileId)) ).toMatchObject([{ userId: fixture.ownerId }]) diff --git a/apps/sim/lib/workspaces/resource-handoff.ts b/apps/sim/lib/workspaces/resource-handoff.ts index 847fabe3410..c5854f7f941 100644 --- a/apps/sim/lib/workspaces/resource-handoff.ts +++ b/apps/sim/lib/workspaces/resource-handoff.ts @@ -19,10 +19,11 @@ import { handoffProjectsForWorkspaceDepartureTx, lockProjectsForResourceDepartureTx, } from '@/lib/projects/resource-handoff' +import { handoffFileCreatorsInTx } from '@/lib/uploads/contexts/workspace/creator-handoff' import { reassignWorkflowOwnershipForWorkspaceMemberRemovalTx } from '@/lib/workspaces/utils' /** Private chat attachments keep their user's lifecycle even when stored under a workspace key. */ -const SHARED_FILE_CONTEXTS = ['workspace', 'knowledge-base', 'execution', 'chat'] +const SHARED_FILE_CONTEXTS = ['workspace', 'knowledge-base', 'execution', 'chat', 'workspace-logos'] /** Finds surviving containers from ownership references, including departures predating handoff. */ export async function listSharedResourceWorkspaceIdsForUser(executor: DbOrTx, userId: string) { @@ -33,7 +34,7 @@ export async function listSharedResourceWorkspaceIdsForUser(executor: DbOrTx, us UNION ALL SELECT ${userTableDefinitions.workspaceId} FROM ${userTableDefinitions} WHERE ${userTableDefinitions.createdBy} = ${userId} UNION ALL SELECT ${knowledgeBase.workspaceId} FROM ${knowledgeBase} WHERE ${knowledgeBase.userId} = ${userId} UNION ALL SELECT ${workspaceFiles.workspaceId} FROM ${workspaceFiles} - WHERE ${workspaceFiles.userId} = ${userId} AND ${workspaceFiles.context} IN ('workspace', 'knowledge-base', 'execution', 'chat') AND ${workspaceFiles.chatId} IS NULL + WHERE ${workspaceFiles.userId} = ${userId} AND ${workspaceFiles.context} IN ('workspace', 'knowledge-base', 'execution', 'chat', 'workspace-logos') AND ${workspaceFiles.chatId} IS NULL UNION ALL SELECT ${workspaceFile.workspaceId} FROM ${workspaceFile} WHERE ${workspaceFile.uploadedBy} = ${userId} UNION ALL SELECT ${workflowMcpServer.workspaceId} FROM ${workflowMcpServer} WHERE ${workflowMcpServer.createdBy} = ${userId} AND NOT ${workflowMcpServer.isPublic} @@ -152,17 +153,16 @@ export async function reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx .update(knowledgeBase) .set({ userId, updatedAt }) .where(and(eq(knowledgeBase.workspaceId, row.id), eq(knowledgeBase.userId, departingUserId))) - await tx - .update(workspaceFiles) - .set({ userId, updatedAt }) - .where( - and( - eq(workspaceFiles.workspaceId, row.id), - eq(workspaceFiles.userId, departingUserId), - inArray(workspaceFiles.context, SHARED_FILE_CONTEXTS), - isNull(workspaceFiles.chatId) - ) - ) + await handoffFileCreatorsInTx( + tx, + and( + eq(workspaceFiles.workspaceId, row.id), + eq(workspaceFiles.userId, departingUserId), + inArray(workspaceFiles.context, SHARED_FILE_CONTEXTS), + isNull(workspaceFiles.chatId) + ), + userId + ) await tx .update(workspaceFile) .set({ uploadedBy: userId }) diff --git a/packages/db/public-share-ownership.integration.ts b/packages/db/public-share-ownership.integration.ts index 9631e081a6a..b5ea29b1aab 100644 --- a/packages/db/public-share-ownership.integration.ts +++ b/packages/db/public-share-ownership.integration.ts @@ -193,7 +193,10 @@ describe('Public share canonical ownership in PostgreSQL', () => { async () => { await file() await share(sql) - await sql`DELETE FROM "user" WHERE id = 'user-a'` + await sql.begin(async (tx) => { + await tx`UPDATE workspace_files SET user_id = 'user-b' WHERE user_id = 'user-a'` + await tx`DELETE FROM "user" WHERE id = 'user-a'` + }) expect(await sql`SELECT token, created_by FROM public_share`).toEqual([ { token: 'share', created_by: null }, ]) From df7d50589a382e84460bf2bd4087cc7cd0a7c1cb Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Wed, 7 Oct 2026 22:07:43 -0700 Subject: [PATCH 035/113] fix(files): align lifecycle fixtures and conflict responses --- .../[id]/members/[memberId]/route.ts | 6 +- .../v1/admin/workspaces/[id]/members/route.ts | 6 +- .../storage/project-accounting.integration.ts | 73 +++++++++++++------ 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts index 00e39fff565..d802ac65b42 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts @@ -310,10 +310,8 @@ export const DELETE = withRouteHandler( workspaceId, }) } catch (error) { - if ( - error instanceof WorkspaceBillingAccountRemovalError || - error instanceof ProjectConflictError - ) { + if (error instanceof ProjectConflictError) return conflictResponse(error.message) + if (error instanceof WorkspaceBillingAccountRemovalError) { return badRequestResponse(error.message) } logger.error('Admin API: Failed to remove workspace member', { error, workspaceId, memberId }) diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts index e9ee009ee54..495ba25b65a 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts @@ -419,10 +419,8 @@ export const DELETE = withRouteHandler( return singleResponse({ removed: true, userId, workspaceId }) } catch (error) { - if ( - error instanceof WorkspaceBillingAccountRemovalError || - error instanceof ProjectConflictError - ) { + if (error instanceof ProjectConflictError) return conflictResponse(error.message) + if (error instanceof WorkspaceBillingAccountRemovalError) { return badRequestResponse(error.message) } logger.error('Admin API: Failed to remove workspace member', { diff --git a/apps/sim/lib/billing/storage/project-accounting.integration.ts b/apps/sim/lib/billing/storage/project-accounting.integration.ts index 2eafa039fb0..02723eaa22d 100644 --- a/apps/sim/lib/billing/storage/project-accounting.integration.ts +++ b/apps/sim/lib/billing/storage/project-accounting.integration.ts @@ -109,10 +109,22 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' entity_id text NOT NULL, permission_type text NOT NULL); CREATE TABLE permission_group (id text PRIMARY KEY, organization_id text, config jsonb, updated_at timestamp); - CREATE TABLE workspace_files (id text PRIMARY KEY, project_id text, - workspace_id text, context text NOT NULL, size_bytes bigint, deleted_at timestamp); - CREATE TABLE workspace_file_version (id text PRIMARY KEY, - file_id text REFERENCES workspace_files(id) ON DELETE CASCADE, size_bytes bigint NOT NULL); + CREATE TABLE "user" (id text PRIMARY KEY); + CREATE TABLE folder (LIKE public.folder INCLUDING DEFAULTS, PRIMARY KEY (id)); + CREATE TABLE workspace_files (LIKE public.workspace_files INCLUDING DEFAULTS, PRIMARY KEY (id)); + ALTER TABLE workspace_files + ALTER COLUMN user_id SET DEFAULT 'user-a', + ALTER COLUMN key SET DEFAULT 'fixture-key', + ALTER COLUMN original_name SET DEFAULT 'fixture.txt', + ALTER COLUMN content_type SET DEFAULT 'text/plain'; + CREATE TABLE workspace_file_version (LIKE public.workspace_file_version INCLUDING DEFAULTS, + PRIMARY KEY (id), FOREIGN KEY (file_id) REFERENCES workspace_files(id) ON DELETE CASCADE, + UNIQUE (file_id, version)); + ALTER TABLE workspace_file_version + ALTER COLUMN version SET DEFAULT 1, + ALTER COLUMN key SET DEFAULT 'history-key', + ALTER COLUMN content_type SET DEFAULT 'text/plain', + ALTER COLUMN source SET DEFAULT 'unknown'; CREATE TABLE knowledge_base (id text PRIMARY KEY, workspace_id text); CREATE TABLE document (id text PRIMARY KEY, knowledge_base_id text, connector_id text, file_size bigint, deleted_at timestamp); @@ -123,7 +135,8 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' beforeEach(async () => { vi.stubEnv('FREE_STORAGE_LIMIT_GB', '1') - await sql`TRUNCATE workspace_file_version, workspace_files, project, workspace, organization, user_stats, member, subscription, project_workspace, permissions, permission_group` + await sql`TRUNCATE "user", folder, workspace_file_version, workspace_files, project, workspace, organization, user_stats, member, subscription, project_workspace, permissions, permission_group` + await sql`INSERT INTO "user" (id) VALUES ('user-a'), ('user-b')` await sql`INSERT INTO organization (id) VALUES ('organization-a'), ('organization-b')` await sql`INSERT INTO user_stats (id, user_id) VALUES ('stats-a', 'user-a'), ('stats-b', 'user-b')` await sql`INSERT INTO project (id, owner_id, organization_id) @@ -301,19 +314,32 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' await releaseWrite.promise }) await Promise.race([written.promise, write]) - const teardown = database.transaction(async (tx) => { - const [connection] = await tx.execute<{ pid: number }>( - query`SELECT pg_backend_pid() AS pid` + const teardown = database + .transaction(async (tx) => { + const [connection] = await tx.execute<{ pid: number }>( + query`SELECT pg_backend_pid() AS pid` + ) + teardownPid.resolve(connection.pid) + await prepareProjectsForAccountDeletion(tx, 'user-a', []) + }) + .then( + () => null, + (error: unknown) => error ) - teardownPid.resolve(connection.pid) - await prepareProjectsForAccountDeletion(tx, 'user-a', []) - }) try { - await waitForDatabaseLock(await teardownPid.promise) + await Promise.race([ + waitForDatabaseLock(await teardownPid.promise), + teardown.then((error) => { + if (error) throw error + throw new Error('Account teardown completed before waiting for the Project write') + }), + ]) } finally { releaseWrite.resolve() + await Promise.all([write, teardown]) } - await Promise.all([write, teardown]) + const teardownError = await teardown + if (teardownError) throw teardownError expect( await sql`SELECT user_id, storage_used_bytes::integer AS bytes FROM user_stats ORDER BY user_id` ).toEqual([ @@ -323,6 +349,9 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' expect((await sql`SELECT owner_id FROM project WHERE id = 'project-a'`)[0].owner_id).toBe( 'user-b' ) + expect( + (await sql`SELECT user_id FROM workspace_files WHERE id = 'project-file'`)[0].user_id + ).toBe('user-b') expect( (await sql`SELECT billed_account_user_id FROM workspace WHERE id = 'workspace-a'`)[0] .billed_account_user_id @@ -404,9 +433,11 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' await database.transaction(async (tx) => { const prepared = await prepareProjectStorageMutationInTx(tx, context()) await tx.execute( - query`INSERT INTO workspace_files VALUES ('file', 'project-a', NULL, 'project', 100, NULL)` + query`INSERT INTO workspace_files (id, project_id, workspace_id, context, size_bytes, deleted_at) VALUES ('file', 'project-a', NULL, 'project', 100, NULL)` + ) + await tx.execute( + query`INSERT INTO workspace_file_version (id, file_id, size_bytes) VALUES ('version', 'file', 500)` ) - await tx.execute(query`INSERT INTO workspace_file_version VALUES ('version', 'file', 500)`) await prepared.applyDelta(100) }) await database.transaction(async (tx) => { @@ -441,11 +472,11 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' check( 'moves exact Project heads once across payers, including archived heads but excluding versions', async () => { - await sql`INSERT INTO workspace_files VALUES + await sql`INSERT INTO workspace_files (id, project_id, workspace_id, context, size_bytes, deleted_at) VALUES ('active', 'project-a', NULL, 'project', 40, NULL), ('archived', 'project-a', NULL, 'project', 60, now()), ('other', 'project-b', NULL, 'project', 25, NULL)` - await sql`INSERT INTO workspace_file_version VALUES ('version', 'active', 900)` + await sql`INSERT INTO workspace_file_version (id, file_id, size_bytes) VALUES ('version', 'active', 900)` await sql`UPDATE organization SET storage_used_bytes = 125 WHERE id = 'organization-a'` await database.transaction((tx) => changeProjectStoragePayersInTx(tx, [ @@ -484,12 +515,12 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' check( 'reconciliation adds Project heads once and refuses incomplete canonical size metadata', async () => { - await sql`INSERT INTO workspace_files VALUES + await sql`INSERT INTO workspace_files (id, project_id, workspace_id, context, size_bytes, deleted_at) VALUES ('workspace-file', NULL, 'workspace-a', 'workspace', 20, NULL), ('project-file', 'project-a', NULL, 'project', 40, NULL), ('archived', 'project-a', NULL, 'project', 60, now()), ('chat', NULL, 'workspace-a', 'mothership', 999, NULL)` - await sql`INSERT INTO workspace_file_version VALUES ('version', 'project-file', 900)` + await sql`INSERT INTO workspace_file_version (id, file_id, size_bytes) VALUES ('version', 'project-file', 900)` const store = createPostgresStorageReconciliationStore(sql) await store.reconcileWorkspaces(['workspace-a']) await store.reconcileOrganization('organization-a') @@ -512,7 +543,7 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' const write = database.transaction(async (tx) => { const prepared = await prepareProjectStorageMutationInTx(tx, context()) await tx.execute( - query`INSERT INTO workspace_files VALUES ('file', 'project-a', NULL, 'project', 40, NULL)` + query`INSERT INTO workspace_files (id, project_id, workspace_id, context, size_bytes, deleted_at) VALUES ('file', 'project-a', NULL, 'project', 40, NULL)` ) await prepared.applyDelta(40) changed.resolve() @@ -549,7 +580,7 @@ describe('Project storage admission, transfer, and reconciliation in PostgreSQL' const write = database.transaction(async (tx) => { const prepared = await prepareProjectStorageMutationInTx(tx, context()) await tx.execute( - query`INSERT INTO workspace_files VALUES ('file', 'project-a', NULL, 'project', 40, NULL)` + query`INSERT INTO workspace_files (id, project_id, workspace_id, context, size_bytes, deleted_at) VALUES ('file', 'project-a', NULL, 'project', 40, NULL)` ) await prepared.applyDelta(40) changed.resolve() From 4e0223e4fa44b56c8f3cabd104454d13c2881778 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 00:37:38 -0700 Subject: [PATCH 036/113] fix(files): preserve empty history and order lifecycle locks --- apps/sim/background/cleanup-soft-deletes.ts | 2 + apps/sim/lib/billing/storage/index.ts | 1 + apps/sim/lib/billing/storage/tracking.ts | 43 +- .../file-versions.integration.ts | 498 +++++++++++++++++- .../contexts/workspace/creator-handoff.ts | 3 +- .../workspace/workspace-file-manager.ts | 9 +- .../workspace/workspace-file-versions.ts | 46 +- .../shared-resource-handoff.integration.ts | 13 +- .../testing/src/mocks/billing-storage.mock.ts | 3 + 9 files changed, 564 insertions(+), 54 deletions(-) diff --git a/apps/sim/background/cleanup-soft-deletes.ts b/apps/sim/background/cleanup-soft-deletes.ts index de60086f94c..bb185880e56 100644 --- a/apps/sim/background/cleanup-soft-deletes.ts +++ b/apps/sim/background/cleanup-soft-deletes.ts @@ -19,6 +19,7 @@ import { and, asc, eq, inArray, isNotNull, isNull, lt, sql } from 'drizzle-orm' import { type CleanupJobPayload, runCleanupWithLimits } from '@/lib/billing/cleanup-dispatcher' import { decrementStorageUsageForBillingContextInTx, + lockWorkspaceStorageForMutationInTx, resolveStorageBillingContext, } from '@/lib/billing/storage' import { @@ -329,6 +330,7 @@ async function deleteExpiredBillableWorkspaceFileRows( try { const deletedCount = await db.transaction(async (tx) => { await lockWorkspaceProject(tx, workspaceId) + await lockWorkspaceStorageForMutationInTx(tx, workspaceId) const billingContext = await resolveStorageBillingContext(workspaceId, tx) await releaseWorkspaceFileVersionsForPurgeInTx( tx, diff --git a/apps/sim/lib/billing/storage/index.ts b/apps/sim/lib/billing/storage/index.ts index 790eb6a97fa..9e4ad54898e 100644 --- a/apps/sim/lib/billing/storage/index.ts +++ b/apps/sim/lib/billing/storage/index.ts @@ -18,6 +18,7 @@ export { incrementAdmittedStorageUsageForBillingContextInTx, incrementStorageUsageForBillingContextInTx, type LegacyStorageUsageDelta, + lockWorkspaceStorageForMutationInTx, maybeNotifyStorageLimitForBillingContext, type WorkspaceStorageUsageDelta, } from './tracking' diff --git a/apps/sim/lib/billing/storage/tracking.ts b/apps/sim/lib/billing/storage/tracking.ts index 5bd585a2f52..1877c46144f 100644 --- a/apps/sim/lib/billing/storage/tracking.ts +++ b/apps/sim/lib/billing/storage/tracking.ts @@ -528,6 +528,29 @@ export async function applyStorageUsageDeltasInTx( return destinationUpdatedUsage } +/** Locks the canonical workspace before existing-file mutations that later change its storage ledger. */ +export async function lockWorkspaceStorageForMutationInTx( + tx: DbOrTx, + workspaceId: string +): Promise { + const [workspacePayer] = await tx + .select({ + billedAccountUserId: workspace.billedAccountUserId, + organizationId: workspace.organizationId, + storageUsedBytes: workspace.storageUsedBytes, + }) + .from(workspace) + .where(eq(workspace.id, workspaceId)) + .for('no key update') + .limit(1) + + if (!workspacePayer) { + throw new Error(`Workspace ${workspaceId} not found for storage accounting`) + } + + return { id: workspaceId, ...workspacePayer } +} + /** * Mutates the durable workspace total and its current routed payer as one * transaction. The workspace row is the serialization point shared with payer @@ -545,25 +568,9 @@ async function mutateWorkspaceStorageUsage( maximumUsage: number | undefined, context: StorageBillingContext ): Promise { - const [workspacePayer] = await tx - .select({ - billedAccountUserId: workspace.billedAccountUserId, - organizationId: workspace.organizationId, - storageUsedBytes: workspace.storageUsedBytes, - }) - .from(workspace) - .where(eq(workspace.id, workspaceId)) - .for('no key update') - .limit(1) - - if (!workspacePayer) { - throw new Error(`Workspace ${workspaceId} not found for storage accounting`) - } + const workspacePayer = await lockWorkspaceStorageForMutationInTx(tx, workspaceId) - const billingEntity = assertWorkspaceStorageContext( - { id: workspaceId, ...workspacePayer }, - context - ) + const billingEntity = assertWorkspaceStorageContext(workspacePayer, context) const currentPayerUsage = await lockStorageUsageForMutation(tx, billingEntity) if (mutation === 'decrement' && workspacePayer.storageUsedBytes < bytes) { diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts index 03933bc9f8b..08aab154772 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts @@ -3,10 +3,14 @@ import { mkdtempSync } from 'node:fs' import { access, mkdir, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import path from 'node:path' +import { inspect } from 'node:util' import { db, dbFor } from '@sim/db' import { organization, outboxEvent, + permissions, + project, + projectWorkspace, user, workspace, workspaceFileSecretProvenance, @@ -14,6 +18,8 @@ import { workspaceFileVersion, } from '@sim/db/schema' import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' +import { sha256Hex } from '@sim/security/hash' +import { createDeferred } from '@sim/testing/helpers/deferred' import { generateId } from '@sim/utils/id' import { and, asc, eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -38,6 +44,7 @@ import { fetchWorkspaceFileBuffer, getWorkspaceFile, getWorkspaceFileWithCurrentVersion, + purgeCreatedWorkspaceFile, updateWorkspaceFileContent, uploadWorkspaceFile, } from '@/lib/uploads/contexts/workspace/workspace-file-manager' @@ -57,7 +64,9 @@ import { readWorkspaceFileVersionText, revertWorkspaceFileVersion, } from '@/lib/workspace-files/application/file-versions' +import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access' import { runCleanupFileVersions } from '@/background/cleanup-file-versions' +import { runCleanupSoftDeletes } from '@/background/cleanup-soft-deletes' import { projectResolvedSecretModelContent } from '@/executor/utils/resolved-secret-content-projection' import { ResolvedSecretTraceRegistry } from '@/executor/utils/resolved-secret-trace-registry' @@ -93,6 +102,23 @@ describe('workspace file version history in PostgreSQL', () => { return { ...ids, fileId: uploaded.id, firstKey: uploaded.key } } + async function unbindWorkspaceProject(workspaceId: string) { + await db.transaction(async (tx) => { + /** Reconstruct a pre-backfill row; runtime transactions retain all enforcement triggers. */ + await tx.execute(sql`SET LOCAL session_replication_role = replica`) + const bindings = await tx + .delete(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, workspaceId)) + .returning() + await tx.delete(project).where( + inArray( + project.id, + bindings.map((binding) => binding.projectId) + ) + ) + }) + } + function versionRows(fileId: string) { return db .select() @@ -116,6 +142,51 @@ describe('workspace file version history in PostgreSQL', () => { return (await fetchWorkspaceFileBuffer({ ...file, key }, { maxBytes: 1024 })).toString() } + async function runContendedFileOperations( + fileId: string, + operations: (() => Promise)[] + ) { + const ready = createDeferred() + const release = createDeferred() + const blocker = db.transaction(async (tx) => { + await tx.select().from(workspaceFiles).where(eq(workspaceFiles.id, fileId)).for('update') + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + ready.resolve(connection.pid) + await release.promise + }) + const blockerPid = await ready.promise + const pending: Promise[]>[] = [] + try { + for (const operation of operations) { + pending.push(Promise.allSettled([operation()])) + await expect + .poll( + async () => + ( + await db.execute( + sql`WITH RECURSIVE blocked(pid) AS ( + SELECT pid FROM pg_stat_activity + WHERE ${blockerPid} = ANY(pg_blocking_pids(pid)) + UNION + SELECT activity.pid FROM pg_stat_activity activity + JOIN blocked ON blocked.pid = ANY(pg_blocking_pids(activity.pid)) + ) SELECT pid FROM blocked` + ) + ).length, + { timeout: 5000 } + ) + .toBe(pending.length) + } + } finally { + release.resolve() + await blocker + const rejected = (await Promise.all(pending)) + .flat() + .filter((result) => result.status === 'rejected') + expect(rejected, inspect(rejected, { depth: 8 })).toEqual([]) + } + } + it('lists a never-rewritten file as an implicit version 1 attributed to its uploader', async () => { const fixture = await seedFile('original') const file = await getWorkspaceFile(fixture.workspaceId, fixture.fileId) @@ -193,12 +264,149 @@ describe('workspace file version history in PostgreSQL', () => { ]) }) - it('keeps an empty shell unversioned until its first content write after handoff', async () => { + it.each([false, true])( + 'preserves empty history across handoff and first-write version one (unknown=%s)', + async (unknown) => { + const fixture = await seedFile('') + if (unknown) + await db + .update(workspaceFiles) + .set({ contentUpdatedAt: new Date(Date.now() + 5000) }) + .where(eq(workspaceFiles.id, fixture.fileId)) + const before = await getWorkspaceFile(fixture.workspaceId, fixture.fileId) + if (!before) throw new Error('file missing') + const [accounting] = await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + const historyBefore = await queryWorkspaceFileVersions(before, { + sortOrder: 'asc', + limit: 10, + }) + expect(historyBefore.versions).toMatchObject([ + { + version: 1, + source: unknown ? 'unknown' : 'upload', + authorUserIds: unknown ? [] : [fixture.aliceId], + size: 0, + }, + ]) + for (const successor of [fixture.bobId, fixture.aliceId, fixture.bobId]) { + await db.transaction((tx) => + handoffFileCreatorsInTx(tx, eq(workspaceFiles.id, fixture.fileId), successor) + ) + } + const after = await getWorkspaceFile(fixture.workspaceId, fixture.fileId) + if (!after) throw new Error('file missing') + expect(after.uploadedBy).toBe(fixture.bobId) + const historyAfter = await queryWorkspaceFileVersions(after, { sortOrder: 'asc', limit: 10 }) + expect(historyAfter.versions).toEqual(historyBefore.versions) + expect( + await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + ).toEqual([accounting]) + expect(await objectExists(fixture.firstKey)).toBe(true) + await updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.bobId, + Buffer.from('first'), + undefined, + { + version: { source: 'api', authorUserId: fixture.bobId }, + secretProvenancePolicy: { mode: 'replace', provenance: { status: 'unknown' } }, + } + ) + expect(await versionRows(fixture.fileId)).toMatchObject([ + { + version: 1, + source: 'api', + authorUserIds: [fixture.bobId], + contentHash: sha256Hex('first'), + restoredFromVersion: null, + secretProvenanceStatus: 'unknown', + secretProvenanceEntries: [], + }, + ]) + const [written] = await versionRows(fixture.fileId) + expect(written.createdAt).toEqual(written.updatedAt) + expect(written.createdAt).not.toEqual(historyBefore.versions[0].createdAt) + expect(await objectExists(fixture.firstKey)).toBe(false) + expect(await readVersionBytes(fixture.workspaceId, fixture.fileId, written.key)).toBe('first') + } + ) + + it.each(['api', 'revert'] as const)( + 'keeps an explicitly recorded empty %s version through handoff and a later write', + async (source) => { + const fixture = await seedFile('') + await updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.aliceId, + Buffer.alloc(0), + undefined, + { + version: { + source: 'api', + authorUserId: fixture.aliceId, + }, + } + ) + if (source === 'revert') { + await updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.aliceId, + Buffer.from('intermediate'), + undefined, + { version: { source: 'api', authorUserId: fixture.aliceId } } + ) + await revertWorkspaceFileVersion.execute({ + principal: { kind: 'session', userId: fixture.aliceId, sessionId: generateId() }, + input: { fileId: fixture.fileId, assertedWorkspaceId: fixture.workspaceId, version: 1 }, + }) + } + const previousRows = await versionRows(fixture.fileId) + const recorded = previousRows[previousRows.length - 1] + await db.transaction((tx) => + handoffFileCreatorsInTx(tx, eq(workspaceFiles.id, fixture.fileId), fixture.bobId) + ) + await updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.bobId, + Buffer.from('later'), + undefined, + { version: { source: 'api', authorUserId: fixture.bobId } } + ) + const rows = await versionRows(fixture.fileId) + expect(rows.map((row) => [row.version, row.source, row.authorUserIds])).toEqual([ + ...previousRows.map((row) => [row.version, row.source, row.authorUserIds]), + [recorded.version + 1, 'api', [fixture.bobId]], + ]) + expect(rows[recorded.version - 1]).toMatchObject({ + key: recorded.key, + contentHash: recorded.contentHash, + restoredFromVersion: source === 'revert' ? 1 : null, + }) + expect(await objectExists(recorded.key)).toBe(true) + expect(await readVersionBytes(fixture.workspaceId, fixture.fileId, recorded.key)).toBe('') + } + ) + + it('keeps an empty storage object still referenced by a retained file after the first write', async () => { const fixture = await seedFile('') + const [original] = await db + .select() + .from(workspaceFiles) + .where(eq(workspaceFiles.id, fixture.fileId)) + await db.insert(workspaceFiles).values({ ...original, id: generateId(), deletedAt: new Date() }) await db.transaction((tx) => handoffFileCreatorsInTx(tx, eq(workspaceFiles.id, fixture.fileId), fixture.bobId) ) - expect(await versionRows(fixture.fileId)).toEqual([]) await updateWorkspaceFileContent( fixture.workspaceId, fixture.fileId, @@ -207,36 +415,272 @@ describe('workspace file version history in PostgreSQL', () => { undefined, { version: { source: 'api', authorUserId: fixture.bobId } } ) - expect(await versionRows(fixture.fileId)).toMatchObject([ - { version: 1, source: 'api', authorUserIds: [fixture.bobId] }, - ]) + expect(await objectExists(fixture.firstKey)).toBe(true) + expect((await versionRows(fixture.fileId)).map((row) => row.version)).toEqual([1]) }) - it('serializes content writes with creator handoff without duplicate or misattributed history', async () => { - const fixture = await seedFile('original') - await Promise.all([ - db.transaction((tx) => - handoffFileCreatorsInTx(tx, eq(workspaceFiles.id, fixture.fileId), fixture.bobId) - ), - updateWorkspaceFileContent( + it.each([ + { first: 'handoff', content: 'original' }, + { first: 'write', content: 'original' }, + { first: 'handoff', content: '' }, + { first: 'write', content: '' }, + ] as const)( + 'serializes full member revocation with a size-changing content write ($first first, original=$content)', + async ({ first, content }) => { + const fixture = await seedFile(content) + await unbindWorkspaceProject(fixture.workspaceId) + await db + .update(workspaceFiles) + .set({ userId: fixture.bobId }) + .where(eq(workspaceFiles.id, fixture.fileId)) + const [usageBefore] = await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + const operations = { + handoff: () => + db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.bobId, + }).then((result) => expect(result.revoked).toBe(true)) + ), + write: () => + updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.aliceId, + Buffer.from('second'), + undefined, + { version: { source: 'api', authorUserId: fixture.aliceId } } + ), + } + await runContendedFileOperations(fixture.fileId, [ + operations[first], + operations[first === 'handoff' ? 'write' : 'handoff'], + ]) + const rows = await versionRows(fixture.fileId) + expect(rows.map((row) => [row.version, row.authorUserIds])).toEqual( + content + ? [ + [1, [fixture.bobId]], + [2, [fixture.aliceId]], + ] + : [[1, [fixture.aliceId]]] + ) + expect(rows.filter((row) => row.supersededAt === null)).toHaveLength(1) + expect(await readVersionBytes(fixture.workspaceId, fixture.fileId, rows[0].key)).toBe( + content || 'second' + ) + expect((await getWorkspaceFile(fixture.workspaceId, fixture.fileId))?.uploadedBy).toBe( + fixture.aliceId + ) + const [usageAfter] = await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + expect(usageAfter.bytes).toBe( + usageBefore.bytes + Buffer.byteLength('second') - Buffer.byteLength(content) + ) + expect( + await db + .select({ id: permissions.id }) + .from(permissions) + .where( + and( + eq(permissions.entityId, fixture.workspaceId), + eq(permissions.entityType, 'workspace'), + eq(permissions.userId, fixture.bobId) + ) + ) + ).toEqual([]) + const current = await getWorkspaceFile(fixture.workspaceId, fixture.fileId) + if (!current) throw new Error('file missing') + expect((await fetchWorkspaceFileBuffer(current, { maxBytes: 1024 })).toString()).toBe( + 'second' + ) + } + ) + + it.each(['handoff', 'restore'] as const)( + 'serializes legacy member revocation with a size-changing restore (%s first)', + async (first) => { + const fixture = await seedFile('original') + await db + .update(workspaceFiles) + .set({ userId: fixture.bobId }) + .where(eq(workspaceFiles.id, fixture.fileId)) + await updateWorkspaceFileContent( fixture.workspaceId, fixture.fileId, - fixture.aliceId, - Buffer.from('second'), + fixture.bobId, + Buffer.from('longer intermediate'), undefined, - { version: { source: 'api', authorUserId: fixture.aliceId } } - ), - ]) - const rows = await versionRows(fixture.fileId) - expect(rows.map((row) => [row.version, row.authorUserIds])).toEqual([ - [1, [fixture.aliceId]], - [2, [fixture.aliceId]], - ]) - expect(rows.filter((row) => row.supersededAt === null)).toHaveLength(1) - expect(await readVersionBytes(fixture.workspaceId, fixture.fileId, rows[0].key)).toBe( - 'original' - ) - }) + { version: { source: 'api', authorUserId: fixture.bobId } } + ) + await unbindWorkspaceProject(fixture.workspaceId) + const [before] = await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + const operations = { + handoff: () => + db.transaction(async (tx) => { + expect( + ( + await revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.bobId, + }) + ).revoked + ).toBe(true) + }), + restore: () => + revertWorkspaceFileVersion.execute({ + principal: { kind: 'session', userId: fixture.aliceId, sessionId: generateId() }, + input: { fileId: fixture.fileId, assertedWorkspaceId: fixture.workspaceId, version: 1 }, + }), + } + await runContendedFileOperations(fixture.fileId, [ + operations[first], + operations[first === 'handoff' ? 'restore' : 'handoff'], + ]) + const current = await getWorkspaceFile(fixture.workspaceId, fixture.fileId) + if (!current) throw new Error('file missing') + expect((await fetchWorkspaceFileBuffer(current, { maxBytes: 1024 })).toString()).toBe( + 'original' + ) + expect(current.uploadedBy).toBe(fixture.aliceId) + expect( + (await versionRows(fixture.fileId)).map((row) => [ + row.version, + row.source, + row.authorUserIds, + ]) + ).toEqual([ + [1, 'upload', [fixture.bobId]], + [2, 'api', [fixture.bobId]], + [3, 'revert', [fixture.aliceId]], + ]) + const [after] = await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + expect(after.bytes).toBe( + before.bytes + Buffer.byteLength('original') - Buffer.byteLength('longer intermediate') + ) + } + ) + + it.each(['handoff', 'purge'] as const)( + 'serializes member revocation with archive rollback (%s first)', + async (first) => { + const fixture = await seedFile('original') + await unbindWorkspaceProject(fixture.workspaceId) + await db + .update(workspaceFiles) + .set({ userId: fixture.bobId }) + .where(eq(workspaceFiles.id, fixture.fileId)) + const file = await getWorkspaceFile(fixture.workspaceId, fixture.fileId) + if (!file) throw new Error('file missing') + const [usageBefore] = await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + const operations = { + handoff: () => + db.transaction(async (tx) => { + const result = await revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.bobId, + }) + expect(result.revoked).toBe(true) + }), + purge: async () => { + const purged = await purgeCreatedWorkspaceFile({ + workspaceId: fixture.workspaceId, + fileId: fixture.fileId, + key: file.key, + expectedName: file.name, + expectedFolderId: file.folderId ?? null, + expectedUpdatedAt: file.updatedAt, + }) + expect(purged).toBe(first === 'purge') + }, + } + await runContendedFileOperations(fixture.fileId, [ + operations[first], + operations[first === 'handoff' ? 'purge' : 'handoff'], + ]) + const retained = await getWorkspaceFile(fixture.workspaceId, fixture.fileId) + const [usageAfter] = await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + expect(usageAfter.bytes).toBe( + usageBefore.bytes - (first === 'purge' ? Buffer.byteLength('original') : 0) + ) + expect(await objectExists(fixture.firstKey)).toBe(first === 'handoff') + if (first === 'purge') { + expect(retained).toBeNull() + expect(await versionRows(fixture.fileId)).toEqual([]) + } else { + expect(retained?.uploadedBy).toBe(fixture.aliceId) + expect((await versionRows(fixture.fileId)).map((row) => row.authorUserIds)).toEqual([ + [fixture.bobId], + ]) + } + } + ) + + it.each(['handoff', 'cleanup'] as const)( + 'serializes member revocation with retention cleanup (%s first)', + async (first) => { + const fixture = await seedFile('original') + await unbindWorkspaceProject(fixture.workspaceId) + await db + .update(workspaceFiles) + .set({ userId: fixture.bobId, deletedAt: new Date(0) }) + .where(eq(workspaceFiles.id, fixture.fileId)) + const [usageBefore] = await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + const operations = { + handoff: () => + db.transaction(async (tx) => { + const result = await revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.bobId, + }) + expect(result.revoked).toBe(true) + }), + cleanup: () => + runCleanupSoftDeletes({ + workspaceIds: [fixture.workspaceId], + plan: 'free', + retentionHours: 1, + label: 'file-handoff-lock-regression', + }), + } + await runContendedFileOperations(fixture.fileId, [ + operations[first], + operations[first === 'handoff' ? 'cleanup' : 'handoff'], + ]) + expect( + await db + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where(eq(workspaceFiles.id, fixture.fileId)) + ).toEqual([]) + expect(await versionRows(fixture.fileId)).toEqual([]) + const [usageAfter] = await db + .select({ bytes: workspace.storageUsedBytes }) + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + expect(usageAfter.bytes).toBe(usageBefore.bytes - Buffer.byteLength('original')) + } + ) it.each([ ['named', 'download'], diff --git a/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts b/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts index ab97544c172..552ce8b178d 100644 --- a/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts +++ b/apps/sim/lib/uploads/contexts/workspace/creator-handoff.ts @@ -7,7 +7,6 @@ import { loadWorkspaceFileVersionHead, materializeWorkspaceFileVersionInTx, } from '@/lib/uploads/contexts/workspace/workspace-file-versions' -import { getWorkspaceFileSize } from '@/lib/uploads/shared/types' /** Freezes implicit history under the writer lock before replacing a shared file's live creator. */ export async function handoffFileCreatorsInTx( @@ -25,7 +24,7 @@ export async function handoffFileCreatorsInTx( for (const file of files) { if (file.context !== 'workspace' && file.context !== 'project') continue const head = await loadWorkspaceFileVersionHead(file.id, tx) - if (isVersionHeadCurrent(head, file) || (!head && getWorkspaceFileSize(file) === 0)) continue + if (isVersionHeadCurrent(head, file)) continue const provenance = file.projectId && file.secretProvenanceVersion === null ? { status: 'unknown' as const, entries: [] } 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 c5134f1dfc3..0ac8b915b30 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -42,6 +42,7 @@ import { import { decrementStorageUsageForBillingContextInTx, incrementStorageUsageForBillingContextInTx, + lockWorkspaceStorageForMutationInTx, maybeNotifyStorageLimitForBillingContext, resolveStorageBillingContext, } from '@/lib/billing/storage' @@ -2151,10 +2152,13 @@ interface CommitFileContentOptions { secretProvenancePolicy?: WorkspaceFileSecretProvenancePolicy } -/** Commits bytes, history and provenance after the caller has locked its canonical billing payer. */ +/** Commits bytes, history and provenance under canonical scope and storage locks. */ async function commitFileContentInTx(tx: DbTransaction, options: CommitFileContentOptions) { const { owner, fileId, staged } = options - if (owner.entityType === 'workspace') await lockWorkspaceProject(tx, owner.entityId) + if (owner.entityType === 'workspace') { + await lockWorkspaceProject(tx, owner.entityId) + await lockWorkspaceStorageForMutationInTx(tx, owner.entityId) + } if (staged.contentHash === null) throw new Error('Content replacement requires staged bytes with a verified content hash') assertStagedFileOwner(owner, staged) @@ -2632,6 +2636,7 @@ export async function purgeCreatedWorkspaceFile(params: { ) const cleanupEventIds = await db.transaction(async (tx) => { await lockWorkspaceProject(tx, params.workspaceId) + await lockWorkspaceStorageForMutationInTx(tx, params.workspaceId) const [lockedFile] = await tx .select({ id: workspaceFiles.id, diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts index 02b5a388838..76d9488c00e 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts @@ -220,13 +220,34 @@ export async function materializeWorkspaceFileVersionInTx( return materialized } +/** Identifies an implicit empty first version frozen by handoff, never an explicit write or restore. */ +function isMaterializedEmptyInitialVersion( + head: WorkspaceFileVersionSummaryRow | undefined, + file: WorkspaceFileRow +): head is WorkspaceFileVersionSummaryRow { + return ( + head !== undefined && + head.fileId === file.id && + head.version === INITIAL_WORKSPACE_FILE_VERSION && + isVersionHeadCurrent(head, file) && + getWorkspaceFileSize(file) === 0 && + head.sizeBytes === 0 && + head.contentHash === null && + (head.source === 'upload' || head.source === 'unknown') && + head.restoredFromVersion === null && + head.createdAt.getTime() === file.contentUpdatedAt.getTime() && + head.updatedAt.getTime() === file.contentUpdatedAt.getTime() + ) +} + /** * Records a committed content write in the file's history. Runs inside the content-write * transaction, under the file row's lock, which serializes version numbering and coalescing * decisions per file. * * An empty file with no history is a shell whose content arrives in this write (a create followed - * by its first content), so the shell is not kept as a version of its own. + * by its first content), so the shell is not kept as a version of its own. A handoff may have + * materialized that implicit shell to freeze attribution; its first write still replaces version 1. */ export async function recordWorkspaceFileVersionInTx( tx: DbTransaction, @@ -240,6 +261,29 @@ export async function recordWorkspaceFileVersionInTx( return { version: 1, releasedKeys: [previous.key] } } + if (isMaterializedEmptyInitialVersion(head, previous)) { + await tx + .update(workspaceFileVersion) + .set({ + ...contentColumns(next, params.nextProvenance), + contentHash: params.contentHash, + source: write.source, + authorUserIds: write.authorUserId ? [write.authorUserId] : [], + restoredFromVersion: write.restoredFromVersion ?? null, + createdAt: now, + updatedAt: now, + }) + .where(eq(workspaceFileVersion.id, head.id)) + const [references] = await tx.execute<{ referenced: boolean }>(sql` + SELECT EXISTS(SELECT 1 FROM ${workspaceFiles} WHERE ${workspaceFiles.key} = ${previous.key}) + OR EXISTS(SELECT 1 FROM ${workspaceFileVersion} WHERE ${workspaceFileVersion.key} = ${previous.key}) AS referenced + `) + return { + version: INITIAL_WORKSPACE_FILE_VERSION, + releasedKeys: references.referenced ? [] : [previous.key], + } + } + if (!head || !isVersionHeadCurrent(head, previous)) { if (!params.previousProvenance) { throw new Error('Outgoing workspace file content needs a provenance snapshot to be versioned') diff --git a/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts b/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts index 842eebeafb7..05c1745d5b6 100644 --- a/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts +++ b/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts @@ -835,9 +835,14 @@ describe('shared resource retention across workspace departure and account erasu } ) - it.each([true, false])( - 'retains organization-only resources with no workspace membership (currentMember=%s)', - async (currentMember) => { + it.each([ + { currentMember: true, sizeBytes: 8 }, + { currentMember: false, sizeBytes: 8 }, + { currentMember: true, sizeBytes: 0 }, + { currentMember: false, sizeBytes: 0 }, + ])( + 'retains organization-only resources with no workspace membership (currentMember=$currentMember, size=$sizeBytes)', + async ({ currentMember, sizeBytes }) => { const fixture = await seedResources(false, false) const organizationId = generateId() const memberId = generateId() @@ -926,7 +931,7 @@ describe('shared resource retention across workspace departure and account erasu key: `project/${binding.projectId}/fixture`, originalName: 'fixture', contentType: 'text/plain', - sizeBytes: 8, + sizeBytes, deletedAt: new Date(), }) if (currentMember) diff --git a/packages/testing/src/mocks/billing-storage.mock.ts b/packages/testing/src/mocks/billing-storage.mock.ts index 9b1de2043a4..334280ec593 100644 --- a/packages/testing/src/mocks/billing-storage.mock.ts +++ b/packages/testing/src/mocks/billing-storage.mock.ts @@ -38,6 +38,7 @@ export const billingStorageMockFns = { mockGetUserStorageLimit: vi.fn(), mockGetUserStorageUsage: vi.fn(), mockApplyStorageUsageDeltasInTx: vi.fn(), + mockLockWorkspaceStorageForMutationInTx: vi.fn(), mockCheckAndIncrementStorageUsageInTx: vi.fn(), mockDecrementStorageUsageForBillingContextInTx: vi.fn(), mockIncrementAdmittedStorageUsageForBillingContextInTx: vi.fn(), @@ -64,6 +65,8 @@ export const billingStorageMock = { getUserStorageLimit: billingStorageMockFns.mockGetUserStorageLimit, getUserStorageUsage: billingStorageMockFns.mockGetUserStorageUsage, applyStorageUsageDeltasInTx: billingStorageMockFns.mockApplyStorageUsageDeltasInTx, + lockWorkspaceStorageForMutationInTx: + billingStorageMockFns.mockLockWorkspaceStorageForMutationInTx, checkAndIncrementStorageUsageInTx: billingStorageMockFns.mockCheckAndIncrementStorageUsageInTx, decrementStorageUsageForBillingContextInTx: billingStorageMockFns.mockDecrementStorageUsageForBillingContextInTx, From e0b359eafc3b903fe0e2314a81fc8d81ea58eb29 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 00:55:56 -0700 Subject: [PATCH 037/113] fix(files): retry restore names claimed by legacy writers --- .../__integration__/file-names.integration.ts | 103 ++++++++++++++++++ .../workspace/workspace-file-manager.ts | 29 ++++- 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts index 56c47a40ee9..fd43476bb60 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts @@ -9,6 +9,7 @@ import path from 'node:path' import { db, dbFor } from '@sim/db' import { copilotChats, organization, user, workspace, workspaceFiles } from '@sim/db/schema' import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' +import { createDeferred } from '@sim/testing/helpers/deferred' import { generateId } from '@sim/utils/id' import { and, eq, inArray, isNull, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -33,6 +34,8 @@ import { workspaceFileNameFolderCondition, } from '@/lib/uploads/contexts/workspace/workspace-file-folder-manager' import { + deleteWorkspaceFile, + fetchWorkspaceFileBuffer, generateWorkspaceFileKey, getWorkspaceFileByName, resolveWorkspaceFileReference, @@ -41,6 +44,7 @@ import { workspaceFileVfsPath, } from '@/lib/uploads/contexts/workspace/workspace-file-manager' import { createWorkspaceFileDelegatedPrincipal } from '@/lib/workspace-files/application/delegated-principal' +import { restoreWorkspaceFileOperation } from '@/lib/workspace-files/application/restore-workspace-file' describe('workspace file names in PostgreSQL', () => { const fixtures: ReturnType[] = [] @@ -197,6 +201,105 @@ describe('workspace file names in PostgreSQL', () => { } }) + it.each([false, true])( + 'restores with a new name when a deployed writer claims the candidate (nested=%s)', + async (nested) => { + const fixture = await seedWorkspace() + const folderId = nested + ? ( + await createWorkspaceFileFolder({ + workspaceId: fixture.workspaceId, + userId: fixture.aliceId, + name: 'Restore target', + }) + ).id + : null + const archived = await upload(fixture.workspaceId, fixture.aliceId, 'restore.txt', folderId) + await deleteWorkspaceFile(fixture.workspaceId, archived.id) + const competitor = await upload(fixture.workspaceId, fixture.aliceId, 'other.txt', folderId) + const triggerName = sql.identifier(`restore_race_${generateId().replaceAll('-', '')}`) + await db.execute(sql` + CREATE FUNCTION ${triggerName}() RETURNS trigger LANGUAGE plpgsql AS $$ + BEGIN + IF NEW.id = TG_ARGV[0] THEN + PERFORM pg_advisory_xact_lock(hashtext('restore-race:' || NEW.id)); + END IF; + RETURN NEW; + END $$ + `) + await db.execute(sql` + CREATE TRIGGER ${triggerName} BEFORE UPDATE OF deleted_at ON ${workspaceFiles} + FOR EACH ROW WHEN (OLD.deleted_at IS NOT NULL AND NEW.deleted_at IS NULL) + EXECUTE FUNCTION ${triggerName}(${sql.raw(`'${archived.id}'`)}) + `) + const ready = createDeferred() + const release = createDeferred() + const blocker = db.transaction(async (tx) => { + await tx.execute( + sql`SELECT pg_advisory_xact_lock(hashtext(${`restore-race:${archived.id}`}))` + ) + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + ready.resolve(connection.pid) + await release.promise + }) + const blockerPid = await ready.promise + const restoration = Promise.allSettled([ + restoreWorkspaceFileOperation.execute({ + principal: { kind: 'session', userId: fixture.aliceId, sessionId: generateId() }, + input: { fileId: archived.id, assertedWorkspaceId: fixture.workspaceId }, + }), + ]) + try { + await expect + .poll( + async () => + ( + await db.execute(sql` + SELECT pid FROM pg_stat_activity WHERE ${blockerPid} = ANY(pg_blocking_pids(pid)) + `) + ).length, + { timeout: 5000 } + ) + .toBe(1) + /** The deployed rename updates only the file row; it does not take the new directory mutex. */ + await db + .update(workspaceFiles) + .set({ originalName: archived.name, updatedAt: new Date() }) + .where( + and( + eq(workspaceFiles.id, competitor.id), + eq(workspaceFiles.workspaceId, fixture.workspaceId), + eq(workspaceFiles.context, 'workspace') + ) + ) + } finally { + release.resolve() + await blocker + await restoration + await db.execute(sql`DROP TRIGGER ${triggerName} ON ${workspaceFiles}`) + await db.execute(sql`DROP FUNCTION ${triggerName}()`) + } + const [result] = await restoration + if (result.status === 'rejected') throw result.reason + expect(result.value.file).toMatchObject({ + id: archived.id, + name: 'restore_restored.txt', + folderId, + key: archived.key, + }) + expect( + (await fetchWorkspaceFileBuffer(result.value.file, { maxBytes: 1024 })).toString() + ).toBe('restore.txt') + expect( + (await getWorkspaceFileByName(fixture.workspaceId, 'restore.txt', { folderId }))?.id + ).toBe(competitor.id) + expect( + (await getWorkspaceFileByName(fixture.workspaceId, 'restore_restored.txt', { folderId })) + ?.id + ).toBe(archived.id) + } + ) + it('falls back to a short-id suffix after 20 numbered copies, including under concurrency', async () => { const fixture = await seedWorkspace() await upload(fixture.workspaceId, fixture.aliceId, 'page.html') 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 0ac8b915b30..2460d9d5aab 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -2694,9 +2694,19 @@ export async function restoreWorkspaceFile(workspaceId: string, fileId: string): if (!ws || ws.archivedAt) { throw new OrchestrationError('validation', 'Cannot restore file into an archived workspace') } - await db.transaction((tx) => - restoreFileInTx(tx, { entityType: 'workspace', entityId: workspaceId }, fileId) - ) + const maxUniqueViolationRetries = 8 + for (let attempt = 0; attempt < maxUniqueViolationRetries; attempt++) { + try { + await db.transaction((tx) => + restoreFileInTx(tx, { entityType: 'workspace', entityId: workspaceId }, fileId) + ) + return + } catch (error) { + if (!(error instanceof FileConflictError) || attempt === maxUniqueViolationRetries - 1) { + throw error + } + } + } } /** Restores a retained head, re-rooting and deduplicating within its canonical owner. */ @@ -2756,6 +2766,19 @@ async function restoreFileInTx( ) ) .returning() + .catch((error: unknown) => { + const nameConstraint = + owner.entityType === 'project' + ? 'workspace_files_project_folder_name_active_unique' + : 'workspace_files_workspace_folder_name_active_unique' + if ( + getPostgresErrorCode(error) === '23505' && + getPostgresConstraintName(error) === nameConstraint + ) { + throw new FileConflictError(originalName) + } + throw error + }) if (!restored) throw new OrchestrationError('conflict', 'File changed during restoration') return restored } From 1eecc187636c79de159c3f312bb1ab93ffff444d Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 10:08:53 -0700 Subject: [PATCH 038/113] fix(files): make staged cleanup safe across database failures --- .../file-versions.integration.ts | 165 ++++++++++++ .../workspace/workspace-file-manager.ts | 250 ++++++++++-------- scripts/check-unused-exports.baseline.json | 5 - 3 files changed, 301 insertions(+), 119 deletions(-) diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts index 08aab154772..260dc762f51 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts @@ -31,6 +31,8 @@ vi.mock('@/lib/uploads/core/setup.server', () => ({ }, })) +import * as storageBilling from '@/lib/billing/storage' +import { OrchestrationError } from '@/lib/core/orchestration/types' import { encryptSecret } from '@/lib/core/security/encryption' import { createKnowledgeAclFixtureIds, @@ -49,6 +51,7 @@ import { uploadWorkspaceFile, } from '@/lib/uploads/contexts/workspace/workspace-file-manager' import type { WorkspaceFileSecretProvenance } from '@/lib/uploads/contexts/workspace/workspace-file-secret-provenance' +import * as storageCleanup from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' import { WORKSPACE_FILE_STORAGE_CLEANUP_OUTBOX_EVENT } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' import { getCurrentWorkspaceFileVersion, @@ -57,6 +60,7 @@ import { queryWorkspaceFileVersions, releaseWorkspaceFileVersionsForPurgeInTx, } from '@/lib/uploads/contexts/workspace/workspace-file-versions' +import * as storageService from '@/lib/uploads/core/storage-service' import { v2FileErrorPolicies } from '@/lib/workspace-files/api' import { presentWorkspaceFileText } from '@/lib/workspace-files/api/text-presenter' import { @@ -187,6 +191,167 @@ describe('workspace file version history in PostgreSQL', () => { } } + it.each([ + { phase: 'upload', deleteUnavailable: false }, + { phase: 'content', deleteUnavailable: false }, + { phase: 'storage', deleteUnavailable: false }, + { phase: 'content', deleteUnavailable: true }, + ] as const)( + 'preserves the original $phase failure and cleans uncommitted bytes when possible (delete unavailable=$deleteUnavailable)', + async ({ phase, deleteUnavailable }) => { + const fixture = await seedFile('original') + const operationError = new OrchestrationError('conflict', 'Staged operation failed') + const deleteFailure = deleteUnavailable + ? vi + .spyOn(storageService, 'deleteFile') + .mockRejectedValueOnce(new Error('Storage unavailable')) + : null + const enqueue = storageCleanup.enqueueWorkspaceFileStorageCleanups + const enqueueFailure = vi + .spyOn(storageCleanup, 'enqueueWorkspaceFileStorageCleanups') + .mockImplementation((executor, ...args) => + executor === db + ? Promise.reject(new Error('Cleanup database unavailable')) + : enqueue(executor, ...args) + ) + const upload = storageService.uploadFile + let stagedKey = '' + const uploadFailure = vi + .spyOn(storageService, 'uploadFile') + .mockImplementation(async (args) => { + const result = await upload(args) + stagedKey = result.key + if (phase === 'storage') throw operationError + return result + }) + const accountingFailure = + phase === 'storage' + ? null + : vi + .spyOn(storageBilling, 'incrementStorageUsageForBillingContextInTx') + .mockRejectedValueOnce(operationError) + try { + const operation = + phase === 'content' + ? updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.aliceId, + Buffer.from('replacement with more bytes'), + undefined, + { version: { source: 'api', authorUserId: fixture.aliceId } } + ) + : uploadWorkspaceFile( + fixture.workspaceId, + fixture.aliceId, + Buffer.from('new upload'), + 'failed.txt', + 'text/plain', + { notifyWorkspaceChange: false } + ) + await expect(operation).rejects.toBe(operationError) + } finally { + deleteFailure?.mockRestore() + accountingFailure?.mockRestore() + uploadFailure.mockRestore() + enqueueFailure.mockRestore() + } + expect(stagedKey).not.toBe('') + expect(await objectExists(stagedKey)).toBe(deleteUnavailable) + const retained = await getWorkspaceFile(fixture.workspaceId, fixture.fileId) + if (!retained) throw new Error('file missing') + expect((await fetchWorkspaceFileBuffer(retained, { maxBytes: 1024 })).toString()).toBe( + 'original' + ) + expect( + await db + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where(eq(workspaceFiles.key, stagedKey)) + ).toEqual([]) + } + ) + + it.each([ + { operation: 'upload', enqueueAvailable: true }, + { operation: 'content', enqueueAvailable: true }, + { operation: 'upload', enqueueAvailable: false }, + { operation: 'content', enqueueAvailable: false }, + ] as const)( + 'retains committed $operation bytes after acknowledgement loss (cleanup database available=$enqueueAvailable)', + async ({ operation, enqueueAvailable }) => { + const fixture = await seedFile('original') + const transaction = db.transaction.bind(db) + const lostAcknowledgement = vi + .spyOn(db, 'transaction') + .mockImplementationOnce(async (callback, config) => { + await transaction(callback, config) + throw new Error('Commit acknowledgement lost') + }) + const enqueue = storageCleanup.enqueueWorkspaceFileStorageCleanups + const enqueueFailure = vi + .spyOn(storageCleanup, 'enqueueWorkspaceFileStorageCleanups') + .mockImplementation((executor, ...args) => + !enqueueAvailable && executor === db + ? Promise.reject(new Error('Cleanup database unavailable')) + : enqueue(executor, ...args) + ) + const upload = storageService.uploadFile + let stagedKey = '' + const capture = vi.spyOn(storageService, 'uploadFile').mockImplementation(async (args) => { + const result = await upload(args) + stagedKey = result.key + return result + }) + try { + const result = + operation === 'content' + ? updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.aliceId, + Buffer.from('committed content'), + undefined, + { version: { source: 'api', authorUserId: fixture.aliceId } } + ) + : uploadWorkspaceFile( + fixture.workspaceId, + fixture.aliceId, + Buffer.from('committed content'), + 'committed.txt', + 'text/plain', + { notifyWorkspaceChange: false } + ) + await expect(result).rejects.toThrow('Commit acknowledgement lost') + } finally { + capture.mockRestore() + enqueueFailure.mockRestore() + lostAcknowledgement.mockRestore() + } + const [committed] = await db + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where(eq(workspaceFiles.key, stagedKey)) + expect(committed?.id).toEqual(expect.any(String)) + const file = await getWorkspaceFile(fixture.workspaceId, committed.id) + if (!file) throw new Error('committed file missing') + expect((await fetchWorkspaceFileBuffer(file, { maxBytes: 1024 })).toString()).toBe( + 'committed content' + ) + expect( + await db + .select({ id: outboxEvent.id }) + .from(outboxEvent) + .where( + and( + eq(outboxEvent.eventType, WORKSPACE_FILE_STORAGE_CLEANUP_OUTBOX_EVENT), + sql`${outboxEvent.payload}->>'key' = ${stagedKey}` + ) + ) + ).toEqual([]) + } + ) + it('lists a never-rewritten file as an implicit version 1 attributed to its uploader', async () => { const fixture = await seedFile('original') const file = await getWorkspaceFile(fixture.workspaceId, fixture.fileId) 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 2460d9d5aab..d5e4067abd0 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -532,20 +532,64 @@ function assertStagedFileOwner(owner: EditableFileOwner, staged: StagedFileConte } } -/** Persists cleanup before attempting deletion, so storage failures remain retryable. */ +/** Discards definitely uncommitted bytes without replacing the operation's original failure. */ async function discardStagedFileContent(staged: StagedFileContent): Promise { assertStagedFileOwner(staged.owner, staged) - const events = await enqueueWorkspaceFileStorageCleanups( - db, - [staged.key], - staged.owner.entityType - ) + let events: string[] + try { + events = await enqueueWorkspaceFileStorageCleanups(db, [staged.key], staged.owner.entityType) + } catch (error) { + logger.warn('Staged cleanup could not be persisted; attempting direct deletion', { + owner: staged.owner, + key: staged.key, + error: describeError(error), + }) + try { + await deleteFile({ key: staged.key, context: staged.owner.entityType }) + } catch (cleanupError) { + logger.error('Uncommitted file content could not be deleted or queued for cleanup', { + owner: staged.owner, + key: staged.key, + error: describeError(cleanupError), + }) + } + return + } await processWorkspaceFileStorageCleanupsNow(events, { owner: staged.owner, reason: 'content commit failed', }) } +/** Keeps possibly committed bytes when the transaction fails after its callback has completed. */ +async function finalizeStagedFileContent( + staged: StagedFileContent, + prepare: (tx: DbTransaction) => Promise +): Promise { + let preparedForCommit = false + try { + return await db.transaction(async (tx) => { + const result = await prepare(tx) + preparedForCommit = true + return result + }) + } catch (error) { + if (preparedForCommit) { + logger.error( + 'File commit outcome is uncertain; retaining staged content for reconciliation', + { + owner: staged.owner, + key: staged.key, + error: describeError(error), + } + ) + } else { + await discardStagedFileContent(staged) + } + throw error + } +} + /** * Upload a file to workspace-scoped storage */ @@ -614,57 +658,48 @@ export async function uploadWorkspaceFile( ...(options?.folderPath === undefined ? { folderId } : {}), }) - let finalized: { - inserted: WorkspaceFileRow - updatedUsage: number | undefined - } - try { - finalized = await db.transaction(async (tx) => { - await lockWorkspaceProject(tx, workspaceId) - await lockFileDirectories(tx, [{ entityType: 'workspace', entityId: workspaceId }]) - let activeFolderId: string | null - if (options?.folderPath !== undefined) { - const folderIndex = await loadActiveFolderPathIndex(workspaceId, 'file', tx) - const resolvedFolderId = resolveFolderPathFromIndex(folderIndex, options.folderPath) - if (resolvedFolderId === undefined) { - throw new OrchestrationError('not_found', 'Target folder not found') - } - activeFolderId = resolvedFolderId - } else { - activeFolderId = await assertWorkspaceFileFolderTarget(workspaceId, folderId, tx) - } - const inserted = await insertWorkspaceFileMetadataInTx(tx, { - id: fileId, - key: uploadResult.key, - userId, - workspaceId, - folderId: activeFolderId, - originalName: uniqueName, - contentType: effectiveContentType, - size: effectiveBuffer.length, - }) - if (!inserted) { - throw new FileConflictError(uniqueName) - } - if (options?.secretProvenance) { - await replaceWorkspaceFileSecretProvenanceInTx( - tx, - inserted.id, - inserted.contentUpdatedAt, - options.secretProvenance - ) + const finalized = await finalizeStagedFileContent(uploadResult, async (tx) => { + await lockWorkspaceProject(tx, workspaceId) + await lockFileDirectories(tx, [{ entityType: 'workspace', entityId: workspaceId }]) + let activeFolderId: string | null + if (options?.folderPath !== undefined) { + const folderIndex = await loadActiveFolderPathIndex(workspaceId, 'file', tx) + const resolvedFolderId = resolveFolderPathFromIndex(folderIndex, options.folderPath) + if (resolvedFolderId === undefined) { + throw new OrchestrationError('not_found', 'Target folder not found') } - const usage = await incrementStorageUsageForBillingContextInTx( + activeFolderId = resolvedFolderId + } else { + activeFolderId = await assertWorkspaceFileFolderTarget(workspaceId, folderId, tx) + } + const inserted = await insertWorkspaceFileMetadataInTx(tx, { + id: fileId, + key: uploadResult.key, + userId, + workspaceId, + folderId: activeFolderId, + originalName: uniqueName, + contentType: effectiveContentType, + size: effectiveBuffer.length, + }) + if (!inserted) { + throw new FileConflictError(uniqueName) + } + if (options?.secretProvenance) { + await replaceWorkspaceFileSecretProvenanceInTx( tx, - storageBillingContext, - effectiveBuffer.length + inserted.id, + inserted.contentUpdatedAt, + options.secretProvenance ) - return { inserted, updatedUsage: usage } - }) - } catch (finalizationError) { - await discardStagedFileContent(uploadResult) - throw finalizationError - } + } + const usage = await incrementStorageUsageForBillingContextInTx( + tx, + storageBillingContext, + effectiveBuffer.length + ) + return { inserted, updatedUsage: usage } + }) void maybeNotifyStorageLimitForBillingContext(storageBillingContext, finalized.updatedUsage) @@ -2323,67 +2358,54 @@ export async function updateWorkspaceFileContent( folderId: fileRecord.folderId, }) - let finalized: { - file: WorkspaceFileRow - sizeDiff: number - updatedUsage: number | undefined - liveDocEventId: string | undefined - storageCleanupEventIds: string[] - currentVersion: number - } - try { - finalized = await db.transaction(async (tx) => { - const committed = await commitFileContentInTx(tx, { - owner: { entityType: 'workspace', entityId: workspaceId }, - fileId, - staged, - ...options, - }) - const { - file: updatedFile, - previous: currentFile, - sizeDiff, - storageCleanupEventIds, - } = committed - let updatedUsage: number | undefined - if (sizeDiff > 0) { - updatedUsage = await incrementStorageUsageForBillingContextInTx( - tx, - storageBillingContext, - sizeDiff - ) - } else if (sizeDiff < 0) { - await decrementStorageUsageForBillingContextInTx( - tx, - storageBillingContext, - Math.abs(sizeDiff) - ) - } - - const liveDocEventId = - options.syncLiveDoc !== false && - (isMarkdownFile({ type: currentFile.contentType, name: currentFile.originalName }) || - isMarkdownFile({ type: updatedFile.contentType, name: updatedFile.originalName })) - ? await enqueueWorkspaceFileLiveDocReconciliation(tx, { - workspaceId, - fileId, - version: updatedFile.contentUpdatedAt.getTime(), - }) - : undefined - - return { - file: updatedFile, - sizeDiff, - updatedUsage, - liveDocEventId, - storageCleanupEventIds, - currentVersion: committed.currentVersion, - } + const finalized = await finalizeStagedFileContent(staged, async (tx) => { + const committed = await commitFileContentInTx(tx, { + owner: { entityType: 'workspace', entityId: workspaceId }, + fileId, + staged, + ...options, }) - } catch (finalizationError) { - await discardStagedFileContent(staged) - throw finalizationError - } + const { + file: updatedFile, + previous: currentFile, + sizeDiff, + storageCleanupEventIds, + } = committed + let updatedUsage: number | undefined + if (sizeDiff > 0) { + updatedUsage = await incrementStorageUsageForBillingContextInTx( + tx, + storageBillingContext, + sizeDiff + ) + } else if (sizeDiff < 0) { + await decrementStorageUsageForBillingContextInTx( + tx, + storageBillingContext, + Math.abs(sizeDiff) + ) + } + + const liveDocEventId = + options.syncLiveDoc !== false && + (isMarkdownFile({ type: currentFile.contentType, name: currentFile.originalName }) || + isMarkdownFile({ type: updatedFile.contentType, name: updatedFile.originalName })) + ? await enqueueWorkspaceFileLiveDocReconciliation(tx, { + workspaceId, + fileId, + version: updatedFile.contentUpdatedAt.getTime(), + }) + : undefined + + return { + file: updatedFile, + sizeDiff, + updatedUsage, + liveDocEventId, + storageCleanupEventIds, + currentVersion: committed.currentVersion, + } + }) if (finalized.sizeDiff !== 0) { void maybeNotifyStorageLimitForBillingContext( diff --git a/scripts/check-unused-exports.baseline.json b/scripts/check-unused-exports.baseline.json index fb7fd72728b..5c03fd1baa0 100644 --- a/scripts/check-unused-exports.baseline.json +++ b/scripts/check-unused-exports.baseline.json @@ -4236,13 +4236,8 @@ "apps/sim/lib/billing/plan-helpers.ts#sqlIsTeam", "apps/sim/lib/billing/plans.ts#getPlanByPriceId", "apps/sim/lib/billing/retention.ts#EffectivePiiStage", - "apps/sim/lib/billing/storage/index.ts#LegacyStorageUsageDelta", - "apps/sim/lib/billing/storage/index.ts#WorkspaceStorageUsageDelta", - "apps/sim/lib/billing/storage/index.ts#checkAndIncrementStorageUsageInTx", - "apps/sim/lib/billing/storage/index.ts#checkStorageQuota", "apps/sim/lib/billing/storage/limits.ts#getStorageLimits", "apps/sim/lib/billing/storage/payer-transfer.ts#WorkspaceStoragePayer", - "apps/sim/lib/billing/storage/tracking.ts#checkAndIncrementStorageUsageInTx", "apps/sim/lib/billing/stripe-client.ts#hasValidStripeCredentials", "apps/sim/lib/billing/stripe-payment-method.ts#getCustomerId", "apps/sim/lib/billing/subscriptions/utils.ts#getEnterpriseTierLimitPerSeat", From bbf557fd8ee5825a3ee88a9e49b54b6ee67383b2 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 10:31:46 -0700 Subject: [PATCH 039/113] fix(files): clean staged content after rejected commits --- .../file-versions.integration.ts | 113 ++++++++++++++++-- .../workspace/workspace-file-manager.ts | 5 +- packages/utils/src/errors.test.ts | 26 ++++ packages/utils/src/errors.ts | 24 ++++ 4 files changed, 159 insertions(+), 9 deletions(-) diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts index 260dc762f51..e5e70e0b0bf 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts @@ -20,6 +20,7 @@ import { import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { sha256Hex } from '@sim/security/hash' import { createDeferred } from '@sim/testing/helpers/deferred' +import { getPostgresErrorCode } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { and, asc, eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -272,21 +273,119 @@ describe('workspace file version history in PostgreSQL', () => { } ) + it.each(['upload', 'content'] as const)( + 'cleans staged %s bytes when PostgreSQL rejects COMMIT after the callback completes', + async (operation) => { + const fixture = await seedFile('original') + const [before] = await db + .select() + .from(workspace) + .where(eq(workspace.id, fixture.workspaceId)) + const triggerName = sql.identifier(`reject_commit_${generateId().replaceAll('-', '')}`) + await db.execute(sql`CREATE FUNCTION ${triggerName}() RETURNS trigger LANGUAGE plpgsql AS $$ + BEGIN + IF NEW.workspace_id = TG_ARGV[0] THEN + RAISE EXCEPTION 'Deferred file constraint rejected COMMIT' USING ERRCODE = '23514'; + END IF; + RETURN NEW; + END; + $$`) + await db.execute(sql`CREATE CONSTRAINT TRIGGER ${triggerName} + AFTER INSERT OR UPDATE ON ${workspaceFiles} DEFERRABLE INITIALLY DEFERRED + FOR EACH ROW EXECUTE FUNCTION ${triggerName}(${sql.raw(`'${fixture.workspaceId}'`)})`) + const transaction = db.transaction.bind(db) + let callbackCompleted = false + const observeCallback = vi + .spyOn(db, 'transaction') + .mockImplementationOnce((callback, config) => + transaction(async (tx) => { + const result = await callback(tx) + callbackCompleted = true + return result + }, config) + ) + const upload = storageService.uploadFile + let stagedKey = '' + const capture = vi.spyOn(storageService, 'uploadFile').mockImplementation(async (args) => { + const result = await upload(args) + stagedKey = result.key + return result + }) + try { + const result = + operation === 'content' + ? updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.aliceId, + Buffer.from('rejected replacement content'), + undefined, + { version: { source: 'api', authorUserId: fixture.aliceId } } + ) + : uploadWorkspaceFile( + fixture.workspaceId, + fixture.aliceId, + Buffer.from('rejected upload'), + 'rejected.txt', + 'text/plain', + { notifyWorkspaceChange: false } + ) + const rejection = await result.catch((error: unknown) => error) + expect(getPostgresErrorCode(rejection)).toBe('23514') + expect(callbackCompleted).toBe(true) + } finally { + capture.mockRestore() + observeCallback.mockRestore() + await db.execute(sql`DROP TRIGGER ${triggerName} ON ${workspaceFiles}`) + await db.execute(sql`DROP FUNCTION ${triggerName}()`) + } + expect(stagedKey).not.toBe('') + expect( + await db + .select({ id: workspaceFiles.id }) + .from(workspaceFiles) + .where(eq(workspaceFiles.key, stagedKey)) + ).toEqual([]) + const [after] = await db.select().from(workspace).where(eq(workspace.id, fixture.workspaceId)) + expect(after.storageUsedBytes).toBe(before.storageUsedBytes) + expect(await versionRows(fixture.fileId)).toEqual([]) + const retained = await getWorkspaceFile(fixture.workspaceId, fixture.fileId) + if (!retained) throw new Error('original file missing') + expect((await fetchWorkspaceFileBuffer(retained, { maxBytes: 1024 })).toString()).toBe( + 'original' + ) + expect(await objectExists(stagedKey)).toBe(false) + expect( + await db + .select({ id: outboxEvent.id }) + .from(outboxEvent) + .where( + and( + eq(outboxEvent.eventType, WORKSPACE_FILE_STORAGE_CLEANUP_OUTBOX_EVENT), + sql`${outboxEvent.payload}->>'key' = ${stagedKey}` + ) + ) + ).toHaveLength(1) + } + ) + it.each([ - { operation: 'upload', enqueueAvailable: true }, - { operation: 'content', enqueueAvailable: true }, - { operation: 'upload', enqueueAvailable: false }, - { operation: 'content', enqueueAvailable: false }, + { operation: 'upload', enqueueAvailable: true, code: undefined }, + { operation: 'content', enqueueAvailable: true, code: undefined }, + { operation: 'upload', enqueueAvailable: false, code: undefined }, + { operation: 'content', enqueueAvailable: false, code: undefined }, + { operation: 'content', enqueueAvailable: true, code: '40003' }, + { operation: 'upload', enqueueAvailable: false, code: 'CONNECTION_CLOSED' }, ] as const)( - 'retains committed $operation bytes after acknowledgement loss (cleanup database available=$enqueueAvailable)', - async ({ operation, enqueueAvailable }) => { + 'retains committed $operation bytes after acknowledgement loss (cleanup database available=$enqueueAvailable, code=$code)', + async ({ operation, enqueueAvailable, code }) => { const fixture = await seedFile('original') const transaction = db.transaction.bind(db) const lostAcknowledgement = vi .spyOn(db, 'transaction') .mockImplementationOnce(async (callback, config) => { await transaction(callback, config) - throw new Error('Commit acknowledgement lost') + throw Object.assign(new Error('Commit acknowledgement lost'), { code }) }) const enqueue = storageCleanup.enqueueWorkspaceFileStorageCleanups const enqueueFailure = vi 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 d5e4067abd0..abb63a8ff85 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -19,6 +19,7 @@ import { getErrorMessage, getPostgresConstraintName, getPostgresErrorCode, + isPostgresCommitRejection, } from '@sim/utils/errors' import { generateShortId } from '@sim/utils/id' import { omit } from '@sim/utils/object' @@ -561,7 +562,7 @@ async function discardStagedFileContent(staged: StagedFileContent): Promise( staged: StagedFileContent, prepare: (tx: DbTransaction) => Promise @@ -574,7 +575,7 @@ async function finalizeStagedFileContent( return result }) } catch (error) { - if (preparedForCommit) { + if (preparedForCommit && !isPostgresCommitRejection(error)) { logger.error( 'File commit outcome is uncertain; retaining staged content for reconciliation', { diff --git a/packages/utils/src/errors.test.ts b/packages/utils/src/errors.test.ts index 168afff1818..8f5361132ca 100644 --- a/packages/utils/src/errors.test.ts +++ b/packages/utils/src/errors.test.ts @@ -4,6 +4,7 @@ import { getPostgresCancellationReason, getPostgresErrorCode, getTransientDatabaseFailure, + isPostgresCommitRejection, } from '@sim/utils/errors' import { describe, expect, it } from 'vitest' @@ -236,3 +237,28 @@ describe('describeError', () => { expect(described?.causeChain?.length).toBeLessThanOrEqual(10) }) }) + +describe('isPostgresCommitRejection', () => { + it('recognizes a deferred constraint rejection through a transaction wrapper', () => { + const rejection = Object.assign(new Error('deferred constraint failed'), { code: '23514' }) + expect(isPostgresCommitRejection(new Error('transaction failed', { cause: rejection }))).toBe( + true + ) + }) + + it.each(['40003', '08007', 'CONNECTION_CLOSED', 'ECONNRESET', undefined])( + 'does not treat uncertain outcome %s as permission to discard staged data', + (code) => { + const error = Object.assign(new Error('commit outcome unavailable'), { code }) + expect(isPostgresCommitRejection(error)).toBe(false) + } + ) + + it('does not replace an outer connection failure with a nested rejection code', () => { + const rejection = Object.assign(new Error('earlier constraint failure'), { code: '23514' }) + const connection = Object.assign(new Error('connection lost', { cause: rejection }), { + code: 'CONNECTION_CLOSED', + }) + expect(isPostgresCommitRejection(connection)).toBe(false) + }) +}) diff --git a/packages/utils/src/errors.ts b/packages/utils/src/errors.ts index 14248fb4e25..863772624ac 100644 --- a/packages/utils/src/errors.ts +++ b/packages/utils/src/errors.ts @@ -30,6 +30,30 @@ export function getPostgresErrorCode(error: unknown): string | undefined { return readPgErrorField(error, 'code') } +const POSTGRES_COMMIT_REJECTION_CODES = new Set([ + '23000', + '23001', + '23502', + '23503', + '23505', + '23514', + '23P01', + '40000', + '40001', + '40002', + '40P01', +]) + +/** + * Recognizes explicit PostgreSQL constraint/rollback rejections at COMMIT. Use only on the + * transaction's rejection after its callback completed, never on errors from post-commit work. + * Unknown outcomes, including 40003 and connection failures, are not safe cleanup authority. + */ +export function isPostgresCommitRejection(error: unknown): boolean { + const code = getPostgresErrorCode(error) + return code !== undefined && POSTGRES_COMMIT_REJECTION_CODES.has(code) +} + const POSTGRES_CANCELLATION_REASONS = [ ['57014', 'canceling statement due to statement timeout', 'statement_timeout'], ['57014', 'canceling statement due to user request', 'user_cancel'], From 4be47721b83628aa5771f79d367605c605ce030c Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 14:01:33 -0700 Subject: [PATCH 040/113] fix(ci): resolve manual audit base from the open pull request --- .github/scripts/resolve-audit-base.sh | 40 +++++++ .github/workflows/checks.yml | 20 ++-- .github/workflows/ci.yml | 3 + scripts/audit-base-workflow.test.ts | 147 ++++++++++++++++++++++++++ 4 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 .github/scripts/resolve-audit-base.sh create mode 100644 scripts/audit-base-workflow.test.ts diff --git a/.github/scripts/resolve-audit-base.sh b/.github/scripts/resolve-audit-base.sh new file mode 100644 index 00000000000..a8b07211705 --- /dev/null +++ b/.github/scripts/resolve-audit-base.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ "$GITHUB_EVENT_NAME" = pull_request ]; then + git fetch --depth=1 origin "$GITHUB_BASE_REF" + echo "ref=origin/$GITHUB_BASE_REF" >> "$GITHUB_OUTPUT" +elif [ "$GITHUB_EVENT_NAME" = workflow_dispatch ]; then + if [ "$GITHUB_REF_TYPE" != branch ]; then + echo 'Manual diff audits require a branch with exactly one open pull request.' >&2 + exit 1 + fi + + pr_pages=$(gh api --method GET "repos/$GITHUB_REPOSITORY/pulls" \ + -f state=open -f "head=${GITHUB_REPOSITORY%%/*}:$GITHUB_REF_NAME" --paginate --slurp) + matching_prs=$(jq -ce --arg repository "$GITHUB_REPOSITORY" --arg branch "$GITHUB_REF_NAME" \ + '[.[][] | select(.state == "open" and .head.ref == $branch and .head.repo.full_name == $repository)]' \ + <<< "$pr_pages") + if [ "$(jq 'length' <<< "$matching_prs")" != 1 ]; then + echo 'Manual diff audits require exactly one open pull request for the dispatched branch.' >&2 + exit 1 + fi + if [ "$(jq -r '.[0].head.sha' <<< "$matching_prs")" != "$GITHUB_SHA" ]; then + echo 'The pull request head changed after dispatch; dispatch again for its current head.' >&2 + exit 1 + fi + + base_sha=$(jq -r '.[0].base.sha' <<< "$matching_prs") + if [[ ! "$base_sha" =~ ^[0-9a-f]{40}$ ]]; then + echo 'The pull request did not provide a valid base commit SHA.' >&2 + exit 1 + fi + git fetch --depth=1 origin "$base_sha" + echo "ref=$base_sha" >> "$GITHUB_OUTPUT" +elif [ -n "${GITHUB_BEFORE:-}" ] && + [ "$GITHUB_BEFORE" != 0000000000000000000000000000000000000000 ]; then + git fetch --depth=1 origin "$GITHUB_BEFORE" + echo "ref=$GITHUB_BEFORE" >> "$GITHUB_OUTPUT" +else + echo 'ref=HEAD~1' >> "$GITHUB_OUTPUT" +fi diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 38704142190..20fddf15f35 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -435,6 +435,9 @@ jobs: # kept off the test shards so neither waits on the other. lint: name: lint + permissions: + contents: read + pull-requests: read runs-on: *runner-4vcpu timeout-minutes: 15 @@ -524,19 +527,14 @@ jobs: # It is fetched by SHA at depth 1; the audits diff two tips and need no # common ancestry. An all-zero `before` means the branch is new and has no # predecessor to diff, so `HEAD~1` remains the fallback there. + # Manual runs pin the actual base SHA of the dispatched branch's unique open PR. + # A merge commit's first parent does not identify a stacked PR's review base. - name: Resolve base ref for diff-based audits id: audit_base - run: | - if [ "${{ github.event_name }}" = "pull_request" ]; then - git fetch --depth=1 origin "${{ github.base_ref }}" - echo "ref=origin/${{ github.base_ref }}" >> "$GITHUB_OUTPUT" - elif [ -n "${{ github.event.before }}" ] && - [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then - git fetch --depth=1 origin "${{ github.event.before }}" - echo "ref=${{ github.event.before }}" >> "$GITHUB_OUTPUT" - else - echo "ref=HEAD~1" >> "$GITHUB_OUTPUT" - fi + env: + GH_TOKEN: ${{ github.token }} + GITHUB_BEFORE: ${{ github.event.before }} + run: bash .github/scripts/resolve-audit-base.sh - name: Check block registry invariants run: bun run apps/sim/scripts/check-block-registry.ts "${{ steps.audit_base.outputs.ref }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2d3ead97d5..efd11fd3d96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,9 @@ permissions: jobs: checks: name: checks + permissions: + contents: read + pull-requests: read if: github.ref != 'refs/heads/dev' || github.event_name == 'pull_request' uses: ./.github/workflows/checks.yml diff --git a/scripts/audit-base-workflow.test.ts b/scripts/audit-base-workflow.test.ts new file mode 100644 index 00000000000..beab80e2cbf --- /dev/null +++ b/scripts/audit-base-workflow.test.ts @@ -0,0 +1,147 @@ +import { spawnSync } from 'node:child_process' +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { afterEach, describe, expect, it } from 'vitest' + +const script = fileURLToPath(new URL('../.github/scripts/resolve-audit-base.sh', import.meta.url)) +const fixtures: string[] = [] + +function git(cwd: string, ...args: string[]): string { + const result = spawnSync('git', args, { cwd, encoding: 'utf8' }) + if (result.status !== 0) throw new Error(`git ${args.join(' ')}: ${result.stderr}`) + return result.stdout.trim() +} + +function fixture() { + const root = mkdtempSync(join(tmpdir(), 'audit-base-')) + fixtures.push(root) + const origin = join(root, 'origin.git') + const repo = join(root, 'repo') + git(root, 'init', '--quiet', '--bare', origin) + git(root, 'init', '--quiet', '-b', 'foundation', repo) + git(repo, 'config', 'user.email', 'test@example.com') + git(repo, 'config', 'user.name', 'Test') + git(repo, 'config', 'commit.gpgsign', 'false') + git(repo, 'remote', 'add', 'origin', origin) + git(repo, 'commit', '--quiet', '--allow-empty', '-m', 'initial') + git(repo, 'branch', 'parent') + writeFileSync(join(repo, 'foundation.sql'), 'SELECT 1;\n') + git(repo, 'add', '.') + git(repo, 'commit', '--quiet', '-m', 'foundation') + const before = git(repo, 'rev-parse', 'HEAD') + git(repo, 'checkout', '--quiet', 'parent') + writeFileSync(join(repo, 'upstream.sql'), 'SELECT 2;\n') + git(repo, 'add', '.') + git(repo, 'commit', '--quiet', '-m', 'upstream') + const base = git(repo, 'rev-parse', 'HEAD') + git(repo, 'checkout', '--quiet', 'foundation') + git(repo, 'merge', '--quiet', '--no-ff', 'parent', '-m', 'merge parent') + const head = git(repo, 'rev-parse', 'HEAD') + git(repo, 'push', '--quiet', 'origin', 'parent', 'foundation') + const pr = { + state: 'open', + head: { ref: 'foundation', sha: head, repo: { full_name: 'example/repo' } }, + base: { sha: base }, + } + return { root, repo, base, before, head, pr } +} + +function resolveBase(data: ReturnType, overrides: Record = {}) { + const output = join(data.root, 'output') + writeFileSync(output, '') + const result = spawnSync( + 'bash', + [ + '--noprofile', + '--norc', + '-c', + 'gh() { printf "%s" "$PR_RESPONSE"; return "$API_EXIT"; }; source "$AUDIT_SCRIPT"', + ], + { + cwd: data.repo, + encoding: 'utf8', + env: { + ...process.env, + AUDIT_SCRIPT: script, + GITHUB_EVENT_NAME: 'workflow_dispatch', + GITHUB_REPOSITORY: 'example/repo', + GITHUB_REF_TYPE: 'branch', + GITHUB_REF_NAME: 'foundation', + GITHUB_SHA: data.head, + GITHUB_BASE_REF: 'parent', + GITHUB_BEFORE: '', + GITHUB_OUTPUT: output, + PR_RESPONSE: JSON.stringify([[data.pr]]), + API_EXIT: '0', + ...overrides, + }, + } + ) + return { ...result, output: readFileSync(output, 'utf8').trim() } +} + +afterEach(() => { + for (const root of fixtures.splice(0)) rmSync(root, { recursive: true, force: true }) +}) + +describe('diff audit base selection', () => { + it('pins the actual PR base for a dispatched merge head instead of its first parent', () => { + const data = fixture() + const result = resolveBase(data) + expect(result.status, result.stderr).toBe(0) + expect(result.output).toBe(`ref=${data.base}`) + expect(git(data.repo, 'diff', '--name-only', data.base, 'HEAD')).toBe('foundation.sql') + expect(git(data.repo, 'diff', '--name-only', data.before, 'HEAD')).toBe('upstream.sql') + }) + + it('fails closed when multiple open PRs across pages claim the dispatched branch', () => { + const data = fixture() + const result = resolveBase(data, { PR_RESPONSE: JSON.stringify([[data.pr], [data.pr]]) }) + expect(result.status).not.toBe(0) + expect(result.output).toBe('') + }) + + it.each(['missing', 'moved', 'foreign', 'tag', 'api', 'invalid-base', 'fetch'] as const)( + 'does not publish an audit base when resolution is %s', + (failure) => { + const data = fixture() + const overrides: Record = {} + if (failure === 'missing') overrides.PR_RESPONSE = '[[]]' + if (failure === 'moved') overrides.GITHUB_SHA = data.before + if (failure === 'foreign') overrides.GITHUB_REPOSITORY = 'another/repo' + if (failure === 'tag') overrides.GITHUB_REF_TYPE = 'tag' + if (failure === 'api') overrides.API_EXIT = '73' + if (failure === 'invalid-base') + overrides.PR_RESPONSE = JSON.stringify([[{ ...data.pr, base: { sha: 'HEAD~1' } }]]) + if (failure === 'fetch') + git(data.repo, 'remote', 'set-url', 'origin', join(data.root, 'missing.git')) + const result = resolveBase(data, overrides) + expect(result.status).not.toBe(0) + expect(result.output).toBe('') + } + ) + + it('keeps the normal PR base branch even if no API association is available', () => { + const data = fixture() + const result = resolveBase(data, { GITHUB_EVENT_NAME: 'pull_request', API_EXIT: '73' }) + expect(result.status, result.stderr).toBe(0) + expect(result.output).toBe('ref=origin/parent') + expect(git(data.repo, 'rev-parse', 'origin/parent')).toBe(data.base) + }) + + it('keeps the pre-push SHA for an existing branch', () => { + const data = fixture() + const result = resolveBase(data, { GITHUB_EVENT_NAME: 'push', GITHUB_BEFORE: data.before }) + expect(result.status, result.stderr).toBe(0) + expect(result.output).toBe(`ref=${data.before}`) + }) + + it('keeps the first-parent fallback for a new branch push', () => { + const data = fixture() + const result = resolveBase(data, { GITHUB_EVENT_NAME: 'push', GITHUB_BEFORE: '0'.repeat(40) }) + expect(result.status, result.stderr).toBe(0) + expect(result.output).toBe('ref=HEAD~1') + }) +}) From e2b8dfa931f39dcdb78be4d2d671b39b3d198ca7 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 14:18:20 -0700 Subject: [PATCH 041/113] fix(tests): create Project membership in desktop and CLI fixtures --- apps/desktop/e2e/fixtures/live-sim.ts | 17 ++++++++++++++--- .../chat/stream/desktop-executor.integration.ts | 5 +++-- .../credential-sharing.integration.ts | 5 +++-- .../desktop/application/activity.integration.ts | 5 +++-- .../desktop/application/executor.integration.ts | 5 +++-- .../desktop/application/import.integration.ts | 5 +++-- .../desktop/executor/bound-turn.integration.ts | 5 +++-- .../desktop-call-classifier.integration.ts | 5 +++-- .../desktop-tool-chat-view-lease.integration.ts | 5 +++-- .../client/desktop-tool-pickup.integration.ts | 5 +++-- .../table/application/row-audit.integration.ts | 5 +++-- apps/sim/scripts/test-cli-run-latency-e2e.ts | 11 +++++++++-- apps/sim/scripts/test-desktop-inbox-e2e.ts | 11 +++++++++-- 13 files changed, 62 insertions(+), 27 deletions(-) diff --git a/apps/desktop/e2e/fixtures/live-sim.ts b/apps/desktop/e2e/fixtures/live-sim.ts index ed3ff12f055..eaeb2b56ce5 100644 --- a/apps/desktop/e2e/fixtures/live-sim.ts +++ b/apps/desktop/e2e/fixtures/live-sim.ts @@ -651,9 +651,16 @@ export class SimDatabase { } async close(): Promise { - if (this.userIds.length > 0) - await this.sql`delete from "user" where id in ${this.sql(this.userIds)}`.catch(() => {}) - await this.sql.end({ timeout: 5 }) + try { + if (this.userIds.length > 0) + await this.sql.begin(async (tx) => { + await tx`delete from workspace where owner_id in ${tx(this.userIds)}` + await tx`delete from project where owner_id in ${tx(this.userIds)}` + await tx`delete from "user" where id in ${tx(this.userIds)}` + }) + } finally { + await this.sql.end({ timeout: 5 }) + } } /** A user with a workspace, a session as the desktop sign-in creates, and one chat per title. */ @@ -671,6 +678,10 @@ export class SimDatabase { await tx`insert into user_stats (id, user_id) values (${generateId()}, ${userId})` await tx`insert into workspace (id, name, owner_id, billed_account_user_id) values (${workspaceId}, 'Desktop tools E2E', ${userId}, ${userId})` + await tx`insert into project (id, name, owner_id) + values (${workspaceId}, 'E2E fixture project', ${userId})` + await tx`insert into project_workspace (project_id, workspace_id) + values (${workspaceId}, ${workspaceId})` await tx`insert into permissions (id, user_id, entity_type, entity_id, permission_type) values (${generateId()}, ${userId}, 'workspace', ${workspaceId}, 'admin')` await tx`insert into session (id, token, user_id, user_agent, expires_at, created_at, updated_at) diff --git a/apps/sim/app/api/copilot/chat/stream/desktop-executor.integration.ts b/apps/sim/app/api/copilot/chat/stream/desktop-executor.integration.ts index 2a0bb1c84bd..b58def23553 100644 --- a/apps/sim/app/api/copilot/chat/stream/desktop-executor.integration.ts +++ b/apps/sim/app/api/copilot/chat/stream/desktop-executor.integration.ts @@ -4,6 +4,7 @@ * Redis through the production reconnect route, for the live tail and the replay batch a view * attaches with after a reload. */ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { authMock, authMockFns } from '@sim/testing/mocks/auth.mock' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -72,7 +73,7 @@ describe.runIf(Boolean(redisUrl))('desktop executor binding on a chat stream', ( createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Desktop stream fixture', ownerId: userId, @@ -105,7 +106,7 @@ describe.runIf(Boolean(redisUrl))('desktop executor binding on a chat stream', ( await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) await db.delete(desktopDevices).where(eq(desktopDevices.id, deviceId)) await db.delete(permissions).where(eq(permissions.userId, userId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) await closeRedisConnection() }) diff --git a/apps/sim/lib/credentials/application/credential-sharing.integration.ts b/apps/sim/lib/credentials/application/credential-sharing.integration.ts index a47432367e2..5134670bcfd 100644 --- a/apps/sim/lib/credentials/application/credential-sharing.integration.ts +++ b/apps/sim/lib/credentials/application/credential-sharing.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' @@ -49,7 +50,7 @@ describe('Credential sharing through user-held API credentials in PostgreSQL', ( role: 'admin', createdAt: new Date(), }) - await db.insert(schema.workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Sharing test', ownerId: actorId, @@ -85,7 +86,7 @@ describe('Credential sharing through user-held API credentials in PostgreSQL', ( afterAll(async () => { if (!runtime) return const { db, schema, eq, inArray } = runtime - await db.delete(schema.workspace).where(eq(schema.workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(schema.workspace.id, workspaceId)) await db.delete(schema.organization).where(eq(schema.organization.id, organizationId)) await db .delete(schema.user) diff --git a/apps/sim/lib/desktop/application/activity.integration.ts b/apps/sim/lib/desktop/application/activity.integration.ts index 37328eaa26b..9f9eadbdcc9 100644 --- a/apps/sim/lib/desktop/application/activity.integration.ts +++ b/apps/sim/lib/desktop/application/activity.integration.ts @@ -2,6 +2,7 @@ * Background desktop activity against real PostgreSQL and Redis: which of a user's chats a desktop * runs, and whether each is running, waiting on the user's approval, or blocked by an offline desktop. */ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { afterAll, describe, expect, it, vi } from 'vitest' const { redisUrl } = await vi.hoisted(async () => { @@ -42,7 +43,7 @@ describe.runIf(Boolean(redisUrl))('background desktop activity', () => { const deviceIds: string[] = [] afterAll(async () => { - if (workspaceIds.length) await db.delete(workspace).where(inArray(workspace.id, workspaceIds)) + if (workspaceIds.length) await deleteWorkspaceFixture(db, inArray(workspace.id, workspaceIds)) if (deviceIds.length) await db.delete(desktopDevices).where(inArray(desktopDevices.id, deviceIds)) if (userIds.length) await db.delete(user).where(inArray(user.id, userIds)) @@ -65,7 +66,7 @@ describe.runIf(Boolean(redisUrl))('background desktop activity', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Desktop activity fixture', ownerId: userId, diff --git a/apps/sim/lib/desktop/application/executor.integration.ts b/apps/sim/lib/desktop/application/executor.integration.ts index 7ef9a70eccf..edaa8ff5cb4 100644 --- a/apps/sim/lib/desktop/application/executor.integration.ts +++ b/apps/sim/lib/desktop/application/executor.integration.ts @@ -4,6 +4,7 @@ * completion. Calls are persisted the way the run loop persists a bound run's desktop calls, and * Stop runs through the real `requestRunStop`. */ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' const { redisUrl, presence } = await vi.hoisted(async () => { @@ -87,7 +88,7 @@ describe.runIf(Boolean(redisUrl))('desktop background executor protocol', () => }) const workspaceId = generateId() workspaceIds.set(userId, workspaceId) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Desktop executor fixture', ownerId: userId, @@ -243,7 +244,7 @@ describe.runIf(Boolean(redisUrl))('desktop background executor protocol', () => await db.delete(desktopDevices).where(inArray(desktopDevices.id, deviceIds)) } const workspaces = [...workspaceIds.values()] - if (workspaces.length) await db.delete(workspace).where(inArray(workspace.id, workspaces)) + if (workspaces.length) await deleteWorkspaceFixture(db, inArray(workspace.id, workspaces)) if (userIds.length) await db.delete(user).where(inArray(user.id, userIds)) }) diff --git a/apps/sim/lib/desktop/application/import.integration.ts b/apps/sim/lib/desktop/application/import.integration.ts index b726197731d..54eabf82b48 100644 --- a/apps/sim/lib/desktop/application/import.integration.ts +++ b/apps/sim/lib/desktop/application/import.integration.ts @@ -8,6 +8,7 @@ import { mkdtempSync } from 'node:fs' import { rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import path from 'node:path' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' const fixtureStorage = vi.hoisted(() => ({ root: '' })) @@ -52,7 +53,7 @@ describe('desktop imports', () => { if (userIds.length) { // Audit rows outlive their actor (the foreign key sets null), so they go first. await db.delete(auditLog).where(inArray(auditLog.actorId, userIds)) - await db.delete(workspace).where(inArray(workspace.ownerId, userIds)) + await deleteWorkspaceFixture(db, inArray(workspace.ownerId, userIds)) await db.delete(user).where(inArray(user.id, userIds)) } await rm(fixtureStorage.root, { recursive: true, force: true }) @@ -75,7 +76,7 @@ describe('desktop imports', () => { updatedAt: now, }) await db.insert(userStats).values({ id: generateId(), userId }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Desktop import fixture', ownerId: userId, diff --git a/apps/sim/lib/desktop/executor/bound-turn.integration.ts b/apps/sim/lib/desktop/executor/bound-turn.integration.ts index 6a13a2be313..fe8db90eb05 100644 --- a/apps/sim/lib/desktop/executor/bound-turn.integration.ts +++ b/apps/sim/lib/desktop/executor/bound-turn.integration.ts @@ -5,6 +5,7 @@ * through the real `requestRunStop`, and a waiter that dies is settled by the stale-execution cron. * Deadlines are moved on the row itself, which is all the server reads. */ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { authMock, authMockFns } from '@sim/testing/mocks/auth.mock' import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' @@ -340,7 +341,7 @@ describe.runIf(Boolean(redisUrl))("a turn bound to a desktop's background execut createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Bound turn fixture', ownerId: userId, @@ -369,7 +370,7 @@ describe.runIf(Boolean(redisUrl))("a turn bound to a desktop's background execut await db.delete(auditLog).where(inArray(auditLog.resourceId, deviceIds)) await db.delete(desktopDevices).where(inArray(desktopDevices.id, deviceIds)) } - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/lib/desktop/executor/desktop-call-classifier.integration.ts b/apps/sim/lib/desktop/executor/desktop-call-classifier.integration.ts index 857d0d7ccfb..d945b4fd700 100644 --- a/apps/sim/lib/desktop/executor/desktop-call-classifier.integration.ts +++ b/apps/sim/lib/desktop/executor/desktop-call-classifier.integration.ts @@ -14,6 +14,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -105,7 +106,7 @@ describe('desktop call classification in SQL and TypeScript', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Classifier parity fixture', ownerId: userId, @@ -154,7 +155,7 @@ describe('desktop call classification in SQL and TypeScript', () => { afterAll(async () => { await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) await db.delete(desktopDevices).where(eq(desktopDevices.id, deviceId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/lib/mothership/tools/client/desktop-tool-chat-view-lease.integration.ts b/apps/sim/lib/mothership/tools/client/desktop-tool-chat-view-lease.integration.ts index a13598bcb6c..c1cd8f4b5db 100644 --- a/apps/sim/lib/mothership/tools/client/desktop-tool-chat-view-lease.integration.ts +++ b/apps/sim/lib/mothership/tools/client/desktop-tool-chat-view-lease.integration.ts @@ -5,6 +5,7 @@ * Runs against real PostgreSQL and Redis through the production pre-persist path, the authorize * route, the lease route and the confirm route. */ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { authMock, authMockFns } from '@sim/testing/mocks/auth.mock' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -202,7 +203,7 @@ describe.runIf(Boolean(redisUrl))('a chat-view import kept alive by its lease', createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Chat-view lease fixture', ownerId: userId, @@ -226,7 +227,7 @@ describe.runIf(Boolean(redisUrl))('a chat-view import kept alive by its lease', afterAll(async () => { await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) await db.delete(user).where(eq(user.id, otherUserId)) }) diff --git a/apps/sim/lib/mothership/tools/client/desktop-tool-pickup.integration.ts b/apps/sim/lib/mothership/tools/client/desktop-tool-pickup.integration.ts index 98a8b3c983b..0a9a5be1efa 100644 --- a/apps/sim/lib/mothership/tools/client/desktop-tool-pickup.integration.ts +++ b/apps/sim/lib/mothership/tools/client/desktop-tool-pickup.integration.ts @@ -6,6 +6,7 @@ * persisted and dispatched by the production stream handlers, the desktop claims it through the * authorize route and reports through the confirm route. */ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { authMock, authMockFns } from '@sim/testing/mocks/auth.mock' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -141,7 +142,7 @@ describe.runIf(Boolean(redisUrl))('a desktop tool call nobody picks up', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Desktop pickup fixture', ownerId: userId, @@ -173,7 +174,7 @@ describe.runIf(Boolean(redisUrl))('a desktop tool call nobody picks up', () => { afterAll(async () => { await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/lib/table/application/row-audit.integration.ts b/apps/sim/lib/table/application/row-audit.integration.ts index 4aebd85ad50..ebc871c5ebf 100644 --- a/apps/sim/lib/table/application/row-audit.integration.ts +++ b/apps/sim/lib/table/application/row-audit.integration.ts @@ -7,6 +7,7 @@ import { userTableRows, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createSessionPrincipal } from '@sim/testing/factories/principal.factory' import { storageServiceMock } from '@sim/testing/mocks/storage-service.mock' import { tableBillingMock, tableBillingMockFns } from '@sim/testing/mocks/table-billing.mock' @@ -54,7 +55,7 @@ describe('table mutation audit history in PostgreSQL', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Table audit fixture', ownerId: userId, @@ -85,7 +86,7 @@ describe('table mutation audit history in PostgreSQL', () => { afterEach(async () => { await db.delete(auditLog).where(eq(auditLog.workspaceId, workspaceId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/scripts/test-cli-run-latency-e2e.ts b/apps/sim/scripts/test-cli-run-latency-e2e.ts index 0c26f73fe11..b7392755697 100644 --- a/apps/sim/scripts/test-cli-run-latency-e2e.ts +++ b/apps/sim/scripts/test-cli-run-latency-e2e.ts @@ -185,6 +185,10 @@ async function seed() { await tx`insert into user_stats (id, user_id) values (${generateId()}, ${userId})` await tx`insert into workspace (id, name, owner_id, billed_account_user_id) values (${workspaceId}, 'CLI latency fixture', ${userId}, ${userId})` + await tx`insert into project (id, name, owner_id) + values (${workspaceId}, 'E2E fixture project', ${userId})` + await tx`insert into project_workspace (project_id, workspace_id) + values (${workspaceId}, ${workspaceId})` await tx`insert into permissions (id, user_id, entity_type, entity_id, permission_type) values (${generateId()}, ${userId}, 'workspace', ${workspaceId}, 'admin')` await tx`insert into api_key (id, user_id, name, key, key_hash, type) @@ -207,8 +211,11 @@ async function cleanup() { // after the logs that reference them. await sql`delete from workflow_execution_logs where workflow_id = ${workflowId}` await sql`delete from workflow_execution_snapshots where workflow_id = ${workflowId}` - await sql`delete from workspace where id = ${workspaceId}` - await sql`delete from "user" where id = ${userId}` + await sql.begin(async (tx) => { + await tx`delete from workspace where id = ${workspaceId}` + await tx`delete from project where id = ${workspaceId}` + await tx`delete from "user" where id = ${userId}` + }) } interface TransportMarks { diff --git a/apps/sim/scripts/test-desktop-inbox-e2e.ts b/apps/sim/scripts/test-desktop-inbox-e2e.ts index 0f107054653..bb7db4b3d42 100644 --- a/apps/sim/scripts/test-desktop-inbox-e2e.ts +++ b/apps/sim/scripts/test-desktop-inbox-e2e.ts @@ -154,6 +154,10 @@ async function signIn(existing?: Desktop): Promise { await tx`insert into user_stats (id, user_id) values (${generateId()}, ${userId})` await tx`insert into workspace (id, name, owner_id, billed_account_user_id) values (${workspaceId}, 'Desktop inbox E2E', ${userId}, ${userId})` + await tx`insert into project (id, name, owner_id) + values (${workspaceId}, 'E2E fixture project', ${userId})` + await tx`insert into project_workspace (project_id, workspace_id) + values (${workspaceId}, ${workspaceId})` await tx`insert into permissions (id, user_id, entity_type, entity_id, permission_type) values (${generateId()}, ${userId}, 'workspace', ${workspaceId}, 'admin')` } @@ -672,8 +676,11 @@ async function cleanup() { if (userIds.length === 0) return await sql`delete from copilot_chats where user_id = any(${userIds})` await sql`delete from desktop_devices where user_id = any(${userIds})` - await sql`delete from workspace where owner_id = any(${userIds})` - await sql`delete from "user" where id = any(${userIds})` + await sql.begin(async (tx) => { + await tx`delete from workspace where owner_id = any(${userIds})` + await tx`delete from project where owner_id = any(${userIds})` + await tx`delete from "user" where id = any(${userIds})` + }) } try { From 57cd6860a2c86ce6c4ba8c19c2c9ab7ecca5872b Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 14:20:33 -0700 Subject: [PATCH 042/113] fix(files): bound affected subtrees and preserve fenced revisions --- .../__integration__/file-names.integration.ts | 107 ++++++++++++++++++ .../workspace-file-folder-manager.ts | 55 ++++----- ...le-folder-version-ownership.integration.ts | 40 ++++--- .../0403_file_folder_version_ownership.sql | 10 ++ .../db/public-share-ownership.integration.ts | 79 +++++++++++-- packages/db/schema.ts | 3 +- 6 files changed, 240 insertions(+), 54 deletions(-) diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts index fd43476bb60..2fb247de306 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts @@ -29,8 +29,11 @@ import { seedKnowledgeAclFixture, } from '@/lib/knowledge/__integration__/seed-source-access-fixture' import { + bulkArchiveWorkspaceFileItems, createWorkspaceFileFolder, fileNameExistsInWorkspaceFolder, + moveWorkspaceFileItems, + updateWorkspaceFileFolder, workspaceFileNameFolderCondition, } from '@/lib/uploads/contexts/workspace/workspace-file-folder-manager' import { @@ -71,6 +74,110 @@ describe('workspace file names in PostgreSQL', () => { return ids } + it.each(['update', 'move', 'archive'] as const)( + 'allows a small subtree %s when unrelated folders exceed the bulk limit', + async (operation) => { + const fixture = await seedWorkspace() + const rootId = generateId() + const targetId = generateId() + const childId = generateId() + await db.execute(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + SELECT ${rootId} || '-' || n, 'Unrelated ' || n, ${fixture.aliceId}, ${fixture.workspaceId}, 'file' + FROM generate_series(1, 5001) n`) + await db.execute(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + VALUES (${rootId}, 'Root', ${fixture.aliceId}, ${fixture.workspaceId}, 'file'), + (${targetId}, 'Target', ${fixture.aliceId}, ${fixture.workspaceId}, 'file')`) + await db.execute(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, parent_id) + VALUES (${childId}, 'Child', ${fixture.aliceId}, ${fixture.workspaceId}, 'file', ${rootId})`) + if (operation === 'update') { + await updateWorkspaceFileFolder({ + workspaceId: fixture.workspaceId, + folderId: rootId, + parentId: targetId, + }) + } else if (operation === 'move') { + await moveWorkspaceFileItems({ + workspaceId: fixture.workspaceId, + folderIds: [rootId], + targetFolderId: targetId, + }) + } else { + await bulkArchiveWorkspaceFileItems({ + workspaceId: fixture.workspaceId, + folderIds: [rootId], + }) + } + const rows = await db.execute<{ id: string; parentId: string | null; archived: boolean }>(sql` + SELECT id, parent_id AS "parentId", deleted_at IS NOT NULL AS archived + FROM folder WHERE id IN (${rootId}, ${childId}) ORDER BY name`) + expect([...rows]).toEqual([ + { id: childId, parentId: rootId, archived: operation === 'archive' }, + { + id: rootId, + parentId: operation === 'archive' ? null : targetId, + archived: operation === 'archive', + }, + ]) + const [unrelated] = await db.execute<{ + count: number + }>(sql`SELECT count(*)::int AS count FROM folder + WHERE workspace_id = ${fixture.workspaceId} AND parent_id IS NULL AND deleted_at IS NULL + AND id NOT IN (${rootId}, ${childId}, ${targetId})`) + expect(unrelated.count).toBe(5001) + } + ) + + it('rejects a move whose actual subtree exceeds the bulk limit without changing its parent', async () => { + const fixture = await seedWorkspace() + const rootId = generateId() + const targetId = generateId() + await db.execute(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + VALUES (${rootId}, 'Root', ${fixture.aliceId}, ${fixture.workspaceId}, 'file'), + (${targetId}, 'Target', ${fixture.aliceId}, ${fixture.workspaceId}, 'file')`) + await db.execute(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, parent_id) + SELECT ${rootId} || '-' || n, 'Child ' || n, ${fixture.aliceId}, ${fixture.workspaceId}, 'file', ${rootId} + FROM generate_series(1, 5000) n`) + await expect( + updateWorkspaceFileFolder({ + workspaceId: fixture.workspaceId, + folderId: rootId, + parentId: targetId, + }) + ).rejects.toMatchObject({ + code: 'validation', + message: 'File operation affects more than 5000 items', + }) + expect([...(await db.execute(sql`SELECT parent_id FROM folder WHERE id = ${rootId}`))]).toEqual( + [{ parent_id: null }] + ) + }) + + it('rejects descendant destinations when unrelated folders exceed the bulk limit', async () => { + const fixture = await seedWorkspace() + const rootId = generateId() + const childId = generateId() + await db.execute(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + SELECT ${rootId} || '-' || n, 'Unrelated ' || n, ${fixture.aliceId}, ${fixture.workspaceId}, 'file' + FROM generate_series(1, 5001) n`) + await db.execute(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + VALUES (${rootId}, 'Root', ${fixture.aliceId}, ${fixture.workspaceId}, 'file')`) + await db.execute(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, parent_id) + VALUES (${childId}, 'Child', ${fixture.aliceId}, ${fixture.workspaceId}, 'file', ${rootId})`) + await expect( + updateWorkspaceFileFolder({ + workspaceId: fixture.workspaceId, + folderId: rootId, + parentId: childId, + }) + ).rejects.toMatchObject({ + code: 'validation', + message: 'Cannot move a folder into one of its descendants', + }) + expect([...(await db.execute(sql`SELECT parent_id FROM folder WHERE id = ${rootId}`))]).toEqual( + [{ parent_id: null }] + ) + }) + function upload(workspaceId: string, userId: string, name: string, folderId?: string | null) { return uploadWorkspaceFile(workspaceId, userId, Buffer.from(name), name, 'text/plain', { folderId, diff --git a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts index 1e1b9dbc7cf..08ef27bbc8c 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts @@ -175,6 +175,32 @@ function assertBulkAffectedItemsWithinLimit(count: number): void { } } +/** Loads only the affected active subtrees, with one extra row to detect the bulk limit. */ +async function selectAffectedFileFolders( + owner: EditableFileOwner, + folderIds: string[], + tx: DbTransaction +): Promise> { + if (folderIds.length === 0) return [] + const ownerCondition = fileFolderOwnerCondition(owner) + const rows = await tx.execute<{ id: string; parentId: string | null }>(sql` + WITH RECURSIVE affected AS ( + SELECT ${folderTable.id}, ${folderTable.parentId} + FROM ${folderTable} + WHERE ${ownerCondition} AND ${inArray(folderTable.id, folderIds)} + AND ${folderTable.deletedAt} IS NULL + UNION + SELECT ${folderTable.id}, ${folderTable.parentId} + FROM ${folderTable} JOIN affected ON ${folderTable.parentId} = affected.id + WHERE ${ownerCondition} AND ${folderTable.deletedAt} IS NULL + ) + SELECT id, parent_id AS "parentId" FROM affected + LIMIT ${MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1} + `) + assertBulkAffectedItemsWithinLimit(rows.length) + return [...rows] +} + /** * Verifies every requested active file/folder belongs to this workspace before a bulk mutation. * This prevents the bulk archive primitive's workspace predicate from silently turning an @@ -906,12 +932,7 @@ async function updateFileFolder( throw new OrchestrationError('validation', 'Folder cannot be its own parent') await assertFileFolderTarget(params.owner, finalParentId, tx) if (params.parentId !== undefined) { - const activeFolders = await tx - .select({ id: folderTable.id, parentId: folderTable.parentId }) - .from(folderTable) - .where(and(ownerCondition, isNull(folderTable.deletedAt))) - .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) - assertBulkAffectedItemsWithinLimit(activeFolders.length) + const activeFolders = await selectAffectedFileFolders(params.owner, [params.folderId], tx) if ( finalParentId && collectDescendantFolderIds(activeFolders, params.folderId).includes(finalParentId) @@ -1060,15 +1081,7 @@ async function moveFileItems( } if (folderIds.length > 0) { - const activeFolders = await tx - .select({ id: folderTable.id, parentId: folderTable.parentId }) - .from(folderTable) - .where( - and(fileFolderOwnerCondition(params.owner), isFileFolder, isNull(folderTable.deletedAt)) - ) - .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) - - assertBulkAffectedItemsWithinLimit(activeFolders.length) + const activeFolders = await selectAffectedFileFolders(params.owner, folderIds, tx) const affectedFolderIds = new Set() @@ -1423,17 +1436,7 @@ async function archiveFileItems( await acquireFileFolderMutationLock(tx, params.owner) await assertFileItemsBelongToOwner(params, tx) - const activeFolders = - explicitFolderIds.length > 0 - ? await tx - .select({ id: folderTable.id, parentId: folderTable.parentId }) - .from(folderTable) - .where( - and(fileFolderOwnerCondition(params.owner), isFileFolder, isNull(folderTable.deletedAt)) - ) - .limit(MAX_WORKSPACE_FILE_BULK_AFFECTED_ITEMS + 1) - : [] - assertBulkAffectedItemsWithinLimit(activeFolders.length) + const activeFolders = await selectAffectedFileFolders(params.owner, explicitFolderIds, tx) const descendantFolderIds = explicitFolderIds.flatMap((folderId) => collectDescendantFolderIds(activeFolders, folderId) ) diff --git a/packages/db/file-folder-version-ownership.integration.ts b/packages/db/file-folder-version-ownership.integration.ts index 3d37fbc8eb6..bb6e03d30af 100644 --- a/packages/db/file-folder-version-ownership.integration.ts +++ b/packages/db/file-folder-version-ownership.integration.ts @@ -24,11 +24,11 @@ describe('file folder and version ownership in PostgreSQL', () => { } } - async function waitForDatabaseLock(pid: number) { + async function waitForDatabaseLock(pid: number, writerPid: number) { for (let attempt = 0; attempt < 100; attempt += 1) { const [state] = await sql<{ waiting: boolean }[]>` SELECT EXISTS ( - SELECT 1 FROM pg_stat_activity WHERE pid = ${pid} AND wait_event_type = 'Lock' + SELECT 1 FROM unnest(pg_blocking_pids(${pid})) blocker WHERE blocker = ${writerPid} ) AS waiting ` if (state.waiting) return @@ -338,12 +338,13 @@ describe('file folder and version ownership in PostgreSQL', () => { await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, project_id) VALUES ('folder-a', 'A', 'user-a', ${workspaceId}, 'file', ${projectId}), ('folder-b', 'B', 'user-a', ${workspaceId}, 'file', ${projectId})` - const moved = createDeferred() + const moved = createDeferred() const release = createDeferred() const firstMove = sql.begin(async (tx) => { await tx`SELECT pg_advisory_xact_lock(hashtextextended(${lockKey}, 0))` await tx`UPDATE folder SET parent_id = 'folder-b' WHERE id = 'folder-a'` - moved.resolve() + const [writer] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + moved.resolve(writer.pid) await release.promise }) const waiting = createDeferred() @@ -360,12 +361,12 @@ describe('file folder and version ownership in PostgreSQL', () => { rejection = expect(secondMove).rejects.toMatchObject({ code: isolation === 'repeatable read' ? '40001' : '23514', }) - await waitForDatabaseLock(await waiting.promise) + await waitForDatabaseLock(await waiting.promise, await moved.promise) } finally { release.resolve() await firstMove + await rejection } - await rejection expect(await sql`SELECT id, parent_id FROM folder ORDER BY id`).toEqual([ { id: 'folder-a', parent_id: 'folder-b' }, { id: 'folder-b', parent_id: null }, @@ -378,12 +379,13 @@ describe('file folder and version ownership in PostgreSQL', () => { async (isolation) => { await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) VALUES ('file', 'workspace', 'user-a', 'workspace-a')` - const inserted = createDeferred() + const inserted = createDeferred() const release = createDeferred() const creation = sql.begin(async (tx) => { await tx`INSERT INTO workspace_file_version (id, file_id, workspace_id) VALUES ('version', 'file', 'workspace-a')` - inserted.resolve() + const [writer] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + inserted.resolve(writer.pid) await release.promise }) const waiting = createDeferred() @@ -399,12 +401,12 @@ describe('file folder and version ownership in PostgreSQL', () => { rejection = expect(transfer).rejects.toMatchObject({ code: isolation === 'repeatable read' ? '40001' : '23514', }) - await waitForDatabaseLock(await waiting.promise) + await waitForDatabaseLock(await waiting.promise, await inserted.promise) } finally { release.resolve() await creation + await rejection } - await rejection const [file] = await sql`SELECT workspace_id FROM workspace_files WHERE id = 'file'` expect(file.workspace_id).toBe('workspace-a') } @@ -415,11 +417,12 @@ describe('file folder and version ownership in PostgreSQL', () => { async (isolation) => { await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) VALUES ('file', 'workspace', 'user-a', 'workspace-a')` - const moved = createDeferred() + const moved = createDeferred() const release = createDeferred() const transfer = sql.begin(async (tx) => { await tx`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'file'` - moved.resolve() + const [writer] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + moved.resolve(writer.pid) await release.promise }) const waiting = createDeferred() @@ -436,12 +439,12 @@ describe('file folder and version ownership in PostgreSQL', () => { rejection = expect(creation).rejects.toMatchObject({ code: isolation === 'repeatable read' ? '40001' : '23514', }) - await waitForDatabaseLock(await waiting.promise) + await waitForDatabaseLock(await waiting.promise, await moved.promise) } finally { release.resolve() await transfer + await rejection } - await rejection expect(await sql`SELECT id FROM workspace_file_version`).toHaveLength(0) } ) @@ -449,12 +452,13 @@ describe('file folder and version ownership in PostgreSQL', () => { it.each(['read committed', 'repeatable read'])( 'protects a Project from concurrent folder creation and deletion at %s isolation', async (isolation) => { - const inserted = createDeferred() + const inserted = createDeferred() const release = createDeferred() const creation = sql.begin(async (tx) => { await tx`INSERT INTO folder (id, name, user_id, resource_type, project_id) VALUES ('folder', 'Docs', 'user-a', 'file', 'project-a')` - inserted.resolve() + const [writer] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + inserted.resolve(writer.pid) await release.promise }) const waiting = createDeferred() @@ -470,12 +474,12 @@ describe('file folder and version ownership in PostgreSQL', () => { rejection = expect(deletion).rejects.toMatchObject({ code: '23503', }) - await waitForDatabaseLock(await waiting.promise) + await waitForDatabaseLock(await waiting.promise, await inserted.promise) } finally { release.resolve() await creation + await rejection } - await rejection expect(await sql`SELECT id FROM folder WHERE id = 'folder'`).toHaveLength(1) } ) diff --git a/packages/db/migrations/0403_file_folder_version_ownership.sql b/packages/db/migrations/0403_file_folder_version_ownership.sql index b37f9482e15..b346e02c1ec 100644 --- a/packages/db/migrations/0403_file_folder_version_ownership.sql +++ b/packages/db/migrations/0403_file_folder_version_ownership.sql @@ -116,6 +116,16 @@ DROP TRIGGER IF EXISTS workspace_files_validate_relations ON workspace_files;--> CREATE CONSTRAINT TRIGGER workspace_files_validate_relations AFTER INSERT OR UPDATE ON workspace_files DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE FUNCTION workspace_files_validate_relations();--> statement-breakpoint +-- Relationship fences advance the row version without changing durable content or its provenance. +-- Real inserts and metadata/content changes must still normalize before downstream triggers run. +CREATE OR REPLACE FUNCTION workspace_file_content_version_millisecond() +RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + IF TG_OP = 'UPDATE' AND NEW IS NOT DISTINCT FROM OLD THEN RETURN NEW; END IF; + NEW.content_updated_at := date_trunc('milliseconds', NEW.content_updated_at); + RETURN NEW; +END; +$$;--> statement-breakpoint CREATE OR REPLACE FUNCTION workspace_file_version_owner_match() RETURNS trigger LANGUAGE plpgsql AS $$ DECLARE diff --git a/packages/db/public-share-ownership.integration.ts b/packages/db/public-share-ownership.integration.ts index 055898fe585..1ddf956d893 100644 --- a/packages/db/public-share-ownership.integration.ts +++ b/packages/db/public-share-ownership.integration.ts @@ -11,6 +11,7 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' const databaseUrl = readTestDatabaseUrl() const migrations = [ + '0358_workspace_file_content_version_precision.sql', '0402_file_entity_ownership.sql', '0403_file_folder_version_ownership.sql', '0404_file_creator_lifetime.sql', @@ -45,10 +46,10 @@ describe('Public share canonical ownership in PostgreSQL', () => { if (statement.trim()) await sql.unsafe(statement) } } - async function waitForDatabaseLock(pid: number) { + async function waitForDatabaseLock(pid: number, writerPid: number) { for (let attempt = 0; attempt < 100; attempt += 1) { const [state] = await sql<{ waiting: boolean }[]>`SELECT EXISTS ( - SELECT 1 FROM pg_stat_activity WHERE pid = ${pid} AND wait_event_type = 'Lock' + SELECT 1 FROM unnest(pg_blocking_pids(${pid})) blocker WHERE blocker = ${writerPid} ) AS waiting` if (state.waiting) return true await sleep(10) @@ -110,11 +111,24 @@ describe('Public share canonical ownership in PostgreSQL', () => { updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456', UNIQUE(resource_type, resource_id) )` + const provenanceMigration = readFileSync( + new URL('./migrations/0283_military_fabian_cortez.sql', import.meta.url), + 'utf8' + ) + for (const statement of provenanceMigration.split('--> statement-breakpoint')) { + if ( + statement.includes('CREATE OR REPLACE FUNCTION "demote_secret_provenance_version"') || + statement.includes('CREATE TRIGGER "workspace_files_secret_provenance_demote"') + ) { + await sql.unsafe(statement) + } + } + await sql`CREATE TABLE workspace_file_secret_provenance (file_id text PRIMARY KEY, content_updated_at timestamp NOT NULL)` for (const migration of migrations) await applyMigration(migration) }) beforeEach(async () => { - await sql`TRUNCATE public_share, workspace_file_version, workspace_files, folder, project, workspace, organization, "user"` + await sql`TRUNCATE workspace_file_secret_provenance, public_share, workspace_file_version, workspace_files, folder, project, workspace, organization, "user"` await sql`INSERT INTO "user" VALUES ('user-a'), ('user-b')` await sql`INSERT INTO workspace VALUES ('workspace-a'), ('workspace-b'), ('same-id')` await sql`INSERT INTO organization VALUES ('organization-a')` @@ -150,6 +164,51 @@ describe('Public share canonical ownership in PostgreSQL', () => { VALUES (${id}, 'file', ${fileId}, ${kind === 'workspace' ? owner : null}, 'user-a', ${id}, ${kind}, ${owner})` } + for (const relationship of ['share', 'version'] as const) { + check( + `${relationship} creation preserves a legacy revision's exact timestamp and tracked provenance`, + async () => { + await sql`ALTER TABLE workspace_files DISABLE TRIGGER workspace_files_content_version_millisecond` + try { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, content_updated_at) + VALUES ('file', 'workspace', 'user-a', 'workspace-a', '2026-10-03 01:02:03.456789')` + } finally { + await sql`ALTER TABLE workspace_files ENABLE TRIGGER workspace_files_content_version_millisecond` + } + await sql`INSERT INTO workspace_file_secret_provenance SELECT id, content_updated_at FROM workspace_files WHERE id = 'file'` + const before = await sql`SELECT content_updated_at::text, secret_provenance_version, key + FROM workspace_files WHERE id = 'file'` + if (relationship === 'share') await share(sql, 'share', 'file', 'workspace-a', 'workspace') + else await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` + expect( + await sql`SELECT content_updated_at::text, secret_provenance_version, key + FROM workspace_files WHERE id = 'file'` + ).toEqual(before) + if (relationship === 'share') { + expect(await sql`SELECT resource_id, entity_id FROM public_share`).toEqual([ + { resource_id: 'file', entity_id: 'workspace-a' }, + ]) + } else { + expect(await sql`SELECT file_id, workspace_id FROM workspace_file_version`).toEqual([ + { file_id: 'file', workspace_id: 'workspace-a' }, + ]) + } + expect( + await sql`SELECT f.content_updated_at = p.content_updated_at AS matches + FROM workspace_files f JOIN workspace_file_secret_provenance p ON p.file_id = f.id` + ).toEqual([{ matches: true }]) + await sql`UPDATE workspace_files SET original_name = 'renamed' WHERE id = 'file'` + expect( + await sql`SELECT content_updated_at::text, secret_provenance_version FROM workspace_files` + ).toEqual([{ content_updated_at: '2026-10-03 01:02:03.456', secret_provenance_version: 1 }]) + await sql`UPDATE workspace_files SET content_updated_at = content_updated_at + INTERVAL '1 millisecond' WHERE id = 'file'` + expect(await sql`SELECT secret_provenance_version FROM workspace_files`).toEqual([ + { secret_provenance_version: null }, + ]) + } + ) + } + check( 'legacy file shares derive the exact workspace and reject a different target owner', async () => { @@ -291,12 +350,13 @@ describe('Public share canonical ownership in PostgreSQL', () => { throw new Error('Deletion ended before acquiring its snapshot') }), ]) - const inserted = createDeferred() + const inserted = createDeferred() const release = createDeferred() const inserting = sql.begin(async (tx) => { await tx`INSERT INTO public_share (id, resource_type, resource_id, token, entity_type, entity_id) VALUES ('share', 'file', 'file', 'share', 'project', 'project-a')` - inserted.resolve() + const [writer] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + inserted.resolve(writer.pid) await release.promise }) let waited = false @@ -310,7 +370,7 @@ describe('Public share canonical ownership in PostgreSQL', () => { throw new Error('Deletion ended before publishing its connection') }), ]) - waited = await waitForDatabaseLock(deletePid) + waited = await waitForDatabaseLock(deletePid, await inserted.promise) } finally { startDelete.resolve() release.resolve() @@ -333,11 +393,12 @@ describe('Public share canonical ownership in PostgreSQL', () => { check(`${isolation}: a new share cannot commit behind target deletion`, async () => { await file() - const deleted = createDeferred() + const deleted = createDeferred() const release = createDeferred() const deleting = sql.begin(async (tx) => { await tx`DELETE FROM workspace_files WHERE id = 'file'` - deleted.resolve() + const [writer] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + deleted.resolve(writer.pid) await release.promise }) await Promise.race([deleted.promise, deleting]) @@ -362,7 +423,7 @@ describe('Public share canonical ownership in PostgreSQL', () => { throw new Error('Insertion ended before publishing its connection') }), ]) - waited = await waitForDatabaseLock(insertPid) + waited = await waitForDatabaseLock(insertPid, await deleted.promise) } finally { release.resolve() await deleting diff --git a/packages/db/schema.ts b/packages/db/schema.ts index d8db493d9a6..753bd9ed58e 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2580,7 +2580,8 @@ export const workspaceFiles = pgTable( * `workspace_file_search_index.source_content_updated_at` came from such a round trip can never be * claimed, indexed, or cleaned up. The default truncates, and the * `workspace_files_content_version_millisecond` trigger enforces it for the writers a default cannot - * reach — explicit `CURRENT_TIMESTAMP` expressions, raw SQL inserts, and any UPDATE. + * reach — explicit `CURRENT_TIMESTAMP` expressions, raw SQL inserts, and updates that change row + * data. Relationship fences that only advance the row version preserve an existing revision. */ contentUpdatedAt: timestamp('content_updated_at') .notNull() From 23b0ad5ffe8f76c6c10939aa2ee7e5e144dcfa72 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 14:30:23 -0700 Subject: [PATCH 043/113] fix(ci): pin the current pull request base branch --- .github/scripts/resolve-audit-base.sh | 9 +++++---- scripts/audit-base-workflow.test.ts | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/scripts/resolve-audit-base.sh b/.github/scripts/resolve-audit-base.sh index a8b07211705..a69e8dc2d01 100644 --- a/.github/scripts/resolve-audit-base.sh +++ b/.github/scripts/resolve-audit-base.sh @@ -24,12 +24,13 @@ elif [ "$GITHUB_EVENT_NAME" = workflow_dispatch ]; then exit 1 fi - base_sha=$(jq -r '.[0].base.sha' <<< "$matching_prs") - if [[ ! "$base_sha" =~ ^[0-9a-f]{40}$ ]]; then - echo 'The pull request did not provide a valid base commit SHA.' >&2 + base_ref=$(jq -er '.[0].base.ref | select(type == "string" and length > 0)' <<< "$matching_prs") + if ! git check-ref-format "refs/heads/$base_ref"; then + echo 'The pull request did not provide a valid base branch.' >&2 exit 1 fi - git fetch --depth=1 origin "$base_sha" + git fetch --depth=1 origin "refs/heads/$base_ref" + base_sha=$(git rev-parse --verify 'FETCH_HEAD^{commit}') echo "ref=$base_sha" >> "$GITHUB_OUTPUT" elif [ -n "${GITHUB_BEFORE:-}" ] && [ "$GITHUB_BEFORE" != 0000000000000000000000000000000000000000 ]; then diff --git a/scripts/audit-base-workflow.test.ts b/scripts/audit-base-workflow.test.ts index beab80e2cbf..07cae7f1fe6 100644 --- a/scripts/audit-base-workflow.test.ts +++ b/scripts/audit-base-workflow.test.ts @@ -43,7 +43,7 @@ function fixture() { const pr = { state: 'open', head: { ref: 'foundation', sha: head, repo: { full_name: 'example/repo' } }, - base: { sha: base }, + base: { ref: 'parent', sha: base }, } return { root, repo, base, before, head, pr } } @@ -96,6 +96,17 @@ describe('diff audit base selection', () => { expect(git(data.repo, 'diff', '--name-only', data.before, 'HEAD')).toBe('upstream.sql') }) + it('pins the current base branch when the PR snapshot still reports an older base SHA', () => { + const data = fixture() + const cachedBase = git(data.repo, 'rev-parse', `${data.base}^`) + const result = resolveBase(data, { + PR_RESPONSE: JSON.stringify([[{ ...data.pr, base: { ...data.pr.base, sha: cachedBase } }]]), + }) + expect(result.status, result.stderr).toBe(0) + expect(result.output).toBe(`ref=${data.base}`) + expect(git(data.repo, 'rev-parse', 'FETCH_HEAD')).toBe(data.base) + }) + it('fails closed when multiple open PRs across pages claim the dispatched branch', () => { const data = fixture() const result = resolveBase(data, { PR_RESPONSE: JSON.stringify([[data.pr], [data.pr]]) }) @@ -114,7 +125,9 @@ describe('diff audit base selection', () => { if (failure === 'tag') overrides.GITHUB_REF_TYPE = 'tag' if (failure === 'api') overrides.API_EXIT = '73' if (failure === 'invalid-base') - overrides.PR_RESPONSE = JSON.stringify([[{ ...data.pr, base: { sha: 'HEAD~1' } }]]) + overrides.PR_RESPONSE = JSON.stringify([ + [{ ...data.pr, base: { ref: 'HEAD~1', sha: 'HEAD~1' } }], + ]) if (failure === 'fetch') git(data.repo, 'remote', 'set-url', 'origin', join(data.root, 'missing.git')) const result = resolveBase(data, overrides) From 4b7ed91db69a5b24fbe9ebfc7baccab794d42e22 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 14:42:40 -0700 Subject: [PATCH 044/113] fix(files): require workspace owners for workspace-scoped files --- .../db/file-entity-ownership.integration.ts | 90 +- .../0408_file_workspace_binding.sql | 11 + .../db/migrations/meta/0408_snapshot.json | 30849 ++++++++++++++++ packages/db/migrations/meta/_journal.json | 7 + packages/db/schema.ts | 4 + .../0034_validate_file_workspace_binding.ts | 28 + packages/db/script-migrations/index.ts | 2 + 7 files changed, 30990 insertions(+), 1 deletion(-) create mode 100644 packages/db/migrations/0408_file_workspace_binding.sql create mode 100644 packages/db/migrations/meta/0408_snapshot.json create mode 100644 packages/db/script-migrations/0034_validate_file_workspace_binding.ts diff --git a/packages/db/file-entity-ownership.integration.ts b/packages/db/file-entity-ownership.integration.ts index 4804c65bb80..08b697daa91 100644 --- a/packages/db/file-entity-ownership.integration.ts +++ b/packages/db/file-entity-ownership.integration.ts @@ -1,4 +1,6 @@ import { readFileSync } from 'node:fs' +import { validateFileWorkspaceBindingMigration } from '@sim/db/script-migrations/0034_validate_file_workspace_binding' +import { runScriptMigrations } from '@sim/db/script-migrations/index' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { createDeferred } from '@sim/testing/helpers/deferred' import { sleep } from '@sim/utils/helpers' @@ -12,13 +14,20 @@ const migration = readFileSync( 'utf8' ) +const workspaceBindingMigration = readFileSync( + new URL('./migrations/0408_file_workspace_binding.sql', import.meta.url), + 'utf8' +) + describe('file entity ownership migration in PostgreSQL', () => { const schemaName = `file_entity_${generateId().replaceAll('-', '')}` let sql: Sql let admin: Sql async function applyMigration() { - for (const statement of migration.split('--> statement-breakpoint')) { + for (const statement of `${migration}\n--> statement-breakpoint\n${workspaceBindingMigration}`.split( + '--> statement-breakpoint' + )) { if (statement.trim()) await sql.unsafe(statement) } } @@ -168,6 +177,85 @@ describe('file entity ownership migration in PostgreSQL', () => { }) }) + it.each(['workspace', 'chat', 'mothership', 'execution', 'workspace-logos'])( + 'requires a workspace owner on insert and update for %s files, including archived files', + async (context) => { + for (const deletedAt of [null, new Date()]) { + await expect(sql`INSERT INTO workspace_files (id, context, user_id, deleted_at) + VALUES ('unowned', ${context}, 'user-a', ${deletedAt})`).rejects.toMatchObject({ + code: '23514', + }) + } + await expect(sql`INSERT INTO workspace_files (id, context, user_id, organization_id) + VALUES ('wrong-owner', ${context}, 'user-a', 'organization-a')`).rejects.toMatchObject({ + code: '23514', + }) + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) + VALUES ('owned', ${context}, 'user-a', 'workspace-a')` + await expect( + sql`UPDATE workspace_files SET workspace_id = NULL WHERE id = 'owned'` + ).rejects.toMatchObject({ code: '23514' }) + await sql`INSERT INTO workspace_files (id, context, user_id) + VALUES ('personal', 'copilot', 'user-a')` + await expect( + sql`UPDATE workspace_files SET context = ${context} WHERE id = 'personal'` + ).rejects.toMatchObject({ code: '23514' }) + expect(await sql`SELECT workspace_id FROM workspace_files WHERE id = 'owned'`).toEqual([ + { workspace_id: 'workspace-a' }, + ]) + } + ) + + it('keeps personal and both knowledge-base owner types valid', async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, organization_id) + VALUES ('avatar', 'profile-pictures', 'user-a', NULL, NULL), + ('personal', 'copilot', 'user-a', NULL, NULL), + ('workspace-kb', 'knowledge-base', 'user-a', 'workspace-a', NULL), + ('organization-kb', 'knowledge-base', 'user-a', NULL, 'organization-a')` + expect( + await sql`SELECT file.id, owner.entity_type, owner.entity_id + FROM workspace_files file CROSS JOIN LATERAL workspace_file_owner( + file.context, file.workspace_id, file.project_id, file.organization_id, file.user_id + ) owner ORDER BY file.id` + ).toEqual([ + { id: 'avatar', entity_type: 'user', entity_id: 'user-a' }, + { id: 'organization-kb', entity_type: 'organization', entity_id: 'organization-a' }, + { id: 'personal', entity_type: 'user', entity_id: 'user-a' }, + { id: 'workspace-kb', entity_type: 'workspace', entity_id: 'workspace-a' }, + ]) + }) + + it('reports retained ownerless files without guessing, then validates after explicit repair and replays safely', async () => { + await sql`ALTER TABLE workspace_files DROP CONSTRAINT workspace_files_workspace_binding_check` + await sql`INSERT INTO workspace_files (id, context, user_id, deleted_at) + VALUES ('legacy-unowned', 'workspace', 'user-a', now())` + await applyMigration() + await applyMigration() + await expect(runScriptMigrations(sql, [validateFileWorkspaceBindingMigration])).rejects.toThrow( + 'Workspace-scoped files are missing workspace_id' + ) + expect(await sql`SELECT name FROM script_migrations`).toEqual([]) + expect(await sql`SELECT workspace_id, user_id, key FROM workspace_files`).toEqual([ + { workspace_id: null, user_id: 'user-a', key: 'unchanged-object-key' }, + ]) + expect( + await sql`SELECT convalidated FROM pg_constraint + WHERE conrelid = 'workspace_files'::regclass + AND conname = 'workspace_files_workspace_binding_check'` + ).toEqual([{ convalidated: false }]) + await sql`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'legacy-unowned'` + await runScriptMigrations(sql, [validateFileWorkspaceBindingMigration]) + await runScriptMigrations(sql, [validateFileWorkspaceBindingMigration]) + expect(await sql`SELECT name FROM script_migrations`).toEqual([ + { name: '0034_validate_file_workspace_binding' }, + ]) + expect( + await sql`SELECT convalidated FROM pg_constraint + WHERE conrelid = 'workspace_files'::regclass + AND conname = 'workspace_files_workspace_binding_check'` + ).toEqual([{ convalidated: true }]) + }) + it('allows independent concurrent file inserts into the same Project', async () => { const inserted = createDeferred() const release = createDeferred() diff --git a/packages/db/migrations/0408_file_workspace_binding.sql b/packages/db/migrations/0408_file_workspace_binding.sql new file mode 100644 index 00000000000..1ed1330badb --- /dev/null +++ b/packages/db/migrations/0408_file_workspace_binding.sql @@ -0,0 +1,11 @@ +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint + WHERE conname = 'workspace_files_workspace_binding_check' + AND conrelid = 'workspace_files'::regclass + ) THEN + ALTER TABLE workspace_files ADD CONSTRAINT workspace_files_workspace_binding_check + CHECK (context NOT IN ('workspace', 'chat', 'mothership', 'execution', 'workspace-logos') + OR workspace_id IS NOT NULL) NOT VALID; + END IF; +END $$; diff --git a/packages/db/migrations/meta/0408_snapshot.json b/packages/db/migrations/meta/0408_snapshot.json new file mode 100644 index 00000000000..fe9255d0425 --- /dev/null +++ b/packages/db/migrations/meta/0408_snapshot.json @@ -0,0 +1,30849 @@ +{ + "id": "ff76360a-4b7e-48b8-943f-7a9cc6980064", + "prevId": "ec46a436-88d2-4e86-a5f5-87711de5b966", + "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_search": { + "name": "embedding_keyword_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 + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "embedding_keyword_search_kb_idx": { + "name": "embedding_keyword_search_kb_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_document_idx": { + "name": "embedding_keyword_search_document_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_keyword_search_content_idx": { + "name": "embedding_keyword_search_content_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_keyword_search_id_embedding_id_fk": { + "name": "embedding_keyword_search_id_embedding_id_fk", + "tableFrom": "embedding_keyword_search", + "tableTo": "embedding", + "columnsFrom": ["id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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.file_search_dependency": { + "name": "file_search_dependency", + "schema": "", + "columns": { + "build_id": { + "name": "build_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "file_search_dependency_file_idx": { + "name": "file_search_dependency_file_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "build_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "file_search_dependency_build_id_workspace_file_search_build_id_fk": { + "name": "file_search_dependency_build_id_workspace_file_search_build_id_fk", + "tableFrom": "file_search_dependency", + "tableTo": "workspace_file_search_build", + "columnsFrom": ["build_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "file_search_dependency_build_id_file_id_pk": { + "name": "file_search_dependency_build_id_file_id_pk", + "columns": ["build_id", "file_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.file_search_dispatch_queue": { + "name": "file_search_dispatch_queue", + "schema": "", + "columns": { + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "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": { + "file_search_dispatch_schedule_idx": { + "name": "file_search_dispatch_schedule_idx", + "columns": [ + { + "expression": "last_dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "first" + }, + { + "expression": "enqueued_at", + "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": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "file_search_dispatch_queue_entity_type_entity_id_pk": { + "name": "file_search_dispatch_queue_entity_type_entity_id_pk", + "columns": ["entity_type", "entity_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "file_search_dispatch_owner_check": { + "name": "file_search_dispatch_owner_check", + "value": "\"file_search_dispatch_queue\".\"entity_type\" IN ('workspace', 'project') AND length(\"file_search_dispatch_queue\".\"entity_id\") > 0" + } + }, + "isRLSEnabled": false + }, + "public.folder": { + "name": "folder", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": false + }, + "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_project_id_idx": { + "name": "folder_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_resource_parent_name_active_unique": { + "name": "folder_project_resource_parent_name_active_unique", + "columns": [ + { + "expression": "project_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 AND \"folder\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "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_project_id_project_id_fk": { + "name": "folder_project_id_project_id_fk", + "tableFrom": "folder", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "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": { + "folder_owner_check": { + "name": "folder_owner_check", + "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" + } + }, + "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 + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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_entity_id_idx": { + "name": "public_share_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "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": { + "public_share_entity_binding_check": { + "name": "public_share_entity_binding_check", + "value": "\n (\"public_share\".\"entity_type\" IS NULL AND \"public_share\".\"entity_id\" IS NULL AND \"public_share\".\"workspace_id\" IS NOT NULL)\n OR (\"public_share\".\"entity_type\" IS NOT NULL AND \"public_share\".\"entity_id\" IS NOT NULL AND char_length(\"public_share\".\"entity_id\") > 0\n AND ((\"public_share\".\"entity_type\" = 'workspace' AND \"public_share\".\"workspace_id\" IS NOT NULL AND \"public_share\".\"entity_id\" = \"public_share\".\"workspace_id\")\n OR (\"public_share\".\"entity_type\" = 'project' AND \"public_share\".\"workspace_id\" IS NULL AND \"public_share\".\"resource_type\" = 'file')))\n " + } + }, + "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.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_entity_type": { + "name": "after_entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "after_entity_id": { + "name": "after_entity_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": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "artifact_key": { + "name": "artifact_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": { + "file_search_build_owner_check": { + "name": "file_search_build_owner_check", + "value": "(\"workspace_file_search_build\".\"entity_type\" IS NULL AND \"workspace_file_search_build\".\"entity_id\" IS NULL AND \"workspace_file_search_build\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_build\".\"entity_type\" = 'workspace' AND \"workspace_file_search_build\".\"entity_id\" = \"workspace_file_search_build\".\"workspace_id\") OR (\"workspace_file_search_build\".\"entity_type\" = 'project' AND length(\"workspace_file_search_build\".\"entity_id\") > 0 AND \"workspace_file_search_build\".\"workspace_id\" IS NULL)" + } + }, + "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": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": { + "file_search_chunk_owner_content_idx": { + "name": "file_search_chunk_owner_content_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "content", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_chunk\".\"entity_type\" = 'project'", + "concurrently": true, + "method": "gin", + "with": { + "fastupdate": "off" + } + }, + "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": { + "file_search_chunk_owner_check": { + "name": "file_search_chunk_owner_check", + "value": "(\"workspace_file_search_chunk\".\"entity_type\" IS NULL AND \"workspace_file_search_chunk\".\"entity_id\" IS NULL AND \"workspace_file_search_chunk\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_chunk\".\"entity_type\" = 'workspace' AND \"workspace_file_search_chunk\".\"entity_id\" = \"workspace_file_search_chunk\".\"workspace_id\") OR (\"workspace_file_search_chunk\".\"entity_type\" = 'project' AND length(\"workspace_file_search_chunk\".\"entity_id\") > 0 AND \"workspace_file_search_chunk\".\"workspace_id\" IS NULL)" + }, + "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": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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": { + "file_search_revision_owner_pending_idx": { + "name": "file_search_revision_owner_pending_idx", + "columns": [ + { + "expression": "coalesce(\"entity_type\", 'workspace')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"entity_id\", \"workspace_id\")", + "asc": true, + "isExpression": 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": true, + "method": "btree", + "with": {} + }, + "file_search_revision_owner_status_idx": { + "name": "file_search_revision_owner_status_idx", + "columns": [ + { + "expression": "coalesce(\"entity_type\", 'workspace')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"entity_id\", \"workspace_id\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "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": { + "file_search_revision_owner_check": { + "name": "file_search_revision_owner_check", + "value": "(\"workspace_file_search_revision\".\"entity_type\" IS NULL AND \"workspace_file_search_revision\".\"entity_id\" IS NULL AND \"workspace_file_search_revision\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_revision\".\"entity_type\" = 'workspace' AND \"workspace_file_search_revision\".\"entity_id\" = \"workspace_file_search_revision\".\"workspace_id\") OR (\"workspace_file_search_revision\".\"entity_type\" = 'project' AND length(\"workspace_file_search_revision\".\"entity_id\") > 0 AND \"workspace_file_search_revision\".\"workspace_id\" IS NULL)" + } + }, + "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": false + }, + "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 + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "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_project_id_idx": { + "name": "workspace_files_project_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} + }, + "workspace_files_project_folder_name_active_unique": { + "name": "workspace_files_project_folder_name_active_unique", + "columns": [ + { + "expression": "project_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\".\"project_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "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_search_owner_keyset_idx": { + "name": "workspace_files_search_owner_keyset_idx", + "columns": [ + { + "expression": "(CASE WHEN \"project_id\" IS NOT NULL THEN 'project' ELSE 'workspace' END)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"project_id\", \"workspace_id\")", + "asc": true, + "isExpression": 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) OR (\"workspace_files\".\"context\" = 'project' AND \"workspace_files\".\"project_id\" IS NOT NULL))", + "concurrently": true, + "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_project_id_project_id_fk": { + "name": "workspace_files_project_id_project_id_fk", + "tableFrom": "workspace_files", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "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_owner_check": { + "name": "workspace_files_owner_check", + "value": "num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\", \"workspace_files\".\"organization_id\") <= 1" + }, + "workspace_files_workspace_binding_check": { + "name": "workspace_files_workspace_binding_check", + "value": "\"workspace_files\".\"context\" NOT IN ('workspace', 'chat', 'mothership', 'execution', 'workspace-logos') OR \"workspace_files\".\"workspace_id\" IS NOT NULL" + }, + "workspace_files_project_binding_check": { + "name": "workspace_files_project_binding_check", + "value": "(\"workspace_files\".\"project_id\" IS NOT NULL) = (\"workspace_files\".\"context\" = 'project') AND (\"workspace_files\".\"project_id\" IS NULL OR \"workspace_files\".\"chat_id\" IS NULL)" + }, + "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", + "project_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" + ] + }, + "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 fb7b2ad238f..e91105dcd2d 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -2850,6 +2850,13 @@ "when": 1791447472728, "tag": "0407_file_search_owner_scope", "breakpoints": true + }, + { + "idx": 408, + "version": "7", + "when": 1791495571766, + "tag": "0408_file_workspace_binding", + "breakpoints": true } ] } diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 753bd9ed58e..4213fbe9025 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2599,6 +2599,10 @@ export const workspaceFiles = pgTable( 'workspace_files_owner_check', sql`num_nonnulls(${table.workspaceId}, ${table.projectId}, ${table.organizationId}) <= 1` ), + workspaceBindingCheck: check( + 'workspace_files_workspace_binding_check', + sql`${table.context} NOT IN ('workspace', 'chat', 'mothership', 'execution', 'workspace-logos') OR ${table.workspaceId} IS NOT NULL` + ), projectBindingCheck: check( 'workspace_files_project_binding_check', sql`(${table.projectId} IS NOT NULL) = (${table.context} = 'project') AND (${table.projectId} IS NULL OR ${table.chatId} IS NULL)` diff --git a/packages/db/script-migrations/0034_validate_file_workspace_binding.ts b/packages/db/script-migrations/0034_validate_file_workspace_binding.ts new file mode 100644 index 00000000000..d182dfaf605 --- /dev/null +++ b/packages/db/script-migrations/0034_validate_file_workspace_binding.ts @@ -0,0 +1,28 @@ +import type { ScriptMigration } from '@sim/db/script-migrations/types' +import { createLogger } from '@sim/logger' + +const logger = createLogger('FileWorkspaceBindingValidation') + +/** Requires explicit repair of legacy ownership before validating the write-time constraint. */ +export const validateFileWorkspaceBindingMigration: ScriptMigration = { + name: '0034_validate_file_workspace_binding', + async up(sql) { + const invalid = await sql<{ id: string; context: string }[]>` + SELECT id, context FROM workspace_files + WHERE context IN ('workspace', 'chat', 'mothership', 'execution', 'workspace-logos') + AND workspace_id IS NULL + ORDER BY id LIMIT 10 + ` + if (invalid.length) { + logger.error('Workspace-scoped files require explicit ownership repair', { files: invalid }) + throw new Error( + 'Workspace-scoped files are missing workspace_id. Review the reported file IDs, establish their canonical workspace from authoritative records, repair explicitly, and rerun. Do not infer ownership from the uploader.' + ) + } + await sql.begin(async (tx) => { + await tx`SET LOCAL lock_timeout = '5s'` + await tx`ALTER TABLE workspace_files + VALIDATE CONSTRAINT workspace_files_workspace_binding_check` + }) + }, +} diff --git a/packages/db/script-migrations/index.ts b/packages/db/script-migrations/index.ts index 10c18997c5a..dfa6834eb7a 100644 --- a/packages/db/script-migrations/index.ts +++ b/packages/db/script-migrations/index.ts @@ -12,6 +12,7 @@ import { scopeKeywordProjectionsMigration } from '@sim/db/script-migrations/0025 import { userTableSchemaForWriteMigration } from '@sim/db/script-migrations/0026_user_table_schema_for_write' import { validateFileOwnershipMigration } from '@sim/db/script-migrations/0031_validate_file_ownership' import { backfillPublicShareEntitiesMigration } from '@sim/db/script-migrations/0033_backfill_public_share_entities' +import { validateFileWorkspaceBindingMigration } from '@sim/db/script-migrations/0034_validate_file_workspace_binding' import type { Sql } from 'postgres' import { backfillTableOrderKeys } from './0001_backfill_table_order_keys' import { backfillPausedBillingAttribution } from './0002_backfill_paused_billing_attribution' @@ -66,6 +67,7 @@ export const scriptMigrations: readonly ScriptMigration[] = [ */ validateFileOwnershipMigration, backfillPublicShareEntitiesMigration, + validateFileWorkspaceBindingMigration, ] /** From 1beb436f1cdac2b0903df57f23e72e31edbd0fda Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 15:26:18 -0700 Subject: [PATCH 045/113] fix(db): make file ownership provisioning replay safe --- .../0406_project_file_upload_sessions.sql | 2 +- ...le-ownership-reconciliation.integration.ts | 123 ++++++++++++++++++ .../db/scripts/reconcile-file-ownership.ts | 1 + 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 packages/db/scripts/file-ownership-reconciliation.integration.ts diff --git a/packages/db/migrations/0406_project_file_upload_sessions.sql b/packages/db/migrations/0406_project_file_upload_sessions.sql index 314d361e6fd..2b82b152aa4 100644 --- a/packages/db/migrations/0406_project_file_upload_sessions.sql +++ b/packages/db/migrations/0406_project_file_upload_sessions.sql @@ -1 +1 @@ -ALTER TYPE "public"."upload_session_purpose" ADD VALUE 'project_file' BEFORE 'table_import'; \ No newline at end of file +ALTER TYPE "public"."upload_session_purpose" ADD VALUE IF NOT EXISTS 'project_file' BEFORE 'table_import'; diff --git a/packages/db/scripts/file-ownership-reconciliation.integration.ts b/packages/db/scripts/file-ownership-reconciliation.integration.ts new file mode 100644 index 00000000000..8d661639ace --- /dev/null +++ b/packages/db/scripts/file-ownership-reconciliation.integration.ts @@ -0,0 +1,123 @@ +import { execFile } from 'node:child_process' +import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { promisify } from 'node:util' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { getPostgresErrorCode } from '@sim/utils/errors' +import { generateId } from '@sim/utils/id' +import { drizzle } from 'drizzle-orm/postgres-js' +import { migrate } from 'drizzle-orm/postgres-js/migrator' +import postgres, { type Sql } from 'postgres' +import { describe, expect, it } from 'vitest' + +const databaseUrl = readTestDatabaseUrl() + +async function withDatabase(run: (sql: Sql, url: string) => Promise) { + const admin = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) + const name = `file_reconciliation_test_${generateId().replaceAll('-', '')}` + let sql: Sql | undefined + try { + await admin`CREATE DATABASE ${admin(name)}` + const url = new URL(databaseUrl) + url.pathname = `/${name}` + sql = postgres(url.toString(), { max: 1, onnotice: () => undefined }) + await run(sql, url.toString()) + } finally { + await sql?.end() + await admin`DROP DATABASE IF EXISTS ${admin(name)}` + await admin.end() + } +} + +describe('file ownership provisioning and interrupted migration recovery', () => { + it('retries an enum addition that committed before its journal insert failed', async () => { + const directory = await mkdtemp(join(tmpdir(), 'file-enum-replay-')) + try { + await mkdir(join(directory, 'meta')) + await writeFile(join(directory, '0000_commit.sql'), 'COMMIT;') + await writeFile( + join(directory, '0001_upload_purpose.sql'), + await readFile( + new URL('../migrations/0406_project_file_upload_sessions.sql', import.meta.url) + ) + ) + await writeFile( + join(directory, 'meta/_journal.json'), + JSON.stringify({ + version: '7', + dialect: 'postgresql', + entries: [ + { idx: 0, version: '7', when: 1, tag: '0000_commit', breakpoints: true }, + { idx: 1, version: '7', when: 2, tag: '0001_upload_purpose', breakpoints: true }, + ], + }) + ) + await withDatabase(async (sql) => { + await sql`CREATE TYPE upload_session_purpose AS ENUM ('workspace_file', 'table_import')` + await sql`CREATE SCHEMA drizzle` + await sql`CREATE TABLE drizzle.__drizzle_migrations ( + id serial PRIMARY KEY, hash text NOT NULL, created_at bigint, + CONSTRAINT reject_enum_journal CHECK (created_at <> 2) + )` + const run = () => migrate(drizzle(sql), { migrationsFolder: directory }) + await expect(run()).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '23514' + ) + expect(await sql`SELECT enum_range(NULL::upload_session_purpose)::text AS labels`).toEqual([ + { labels: '{workspace_file,project_file,table_import}' }, + ]) + expect(await sql`SELECT created_at::int FROM drizzle.__drizzle_migrations`).toEqual([ + { created_at: 1 }, + ]) + await sql`ALTER TABLE drizzle.__drizzle_migrations DROP CONSTRAINT reject_enum_journal` + await run() + await run() + expect( + await sql`SELECT created_at::int FROM drizzle.__drizzle_migrations ORDER BY created_at` + ).toEqual([{ created_at: 1 }, { created_at: 2 }]) + expect(await sql`SELECT enum_range(NULL::upload_session_purpose)::text AS labels`).toEqual([ + { labels: '{workspace_file,project_file,table_import}' }, + ]) + }) + } finally { + await rm(directory, { recursive: true, force: true }) + } + }) + + it('reconciles missing precision triggers while preserving legacy no-op fences', async () => { + await withDatabase(async (sql, url) => { + const run = (script: string) => + promisify(execFile)('bun', ['--no-env-file', script], { + cwd: new URL('..', import.meta.url), + env: { ...process.env, DATABASE_URL: url, MIGRATION_DATABASE_URL: url }, + timeout: 120_000, + }) + await run('scripts/migrate.ts') + await sql`DROP TRIGGER workspace_files_content_version_millisecond ON workspace_files` + await sql`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at) + VALUES ('owner', 'Owner', 'owner@example.com', false, now(), now())` + await sql`INSERT INTO workspace_files + (id, key, user_id, context, original_name, content_type, content_updated_at) + VALUES ('legacy', 'legacy-key', 'owner', 'copilot', 'legacy.txt', 'text/plain', + '2026-08-01 03:30:51.566952')` + await run('scripts/reconcile-file-ownership.ts') + await run('scripts/reconcile-file-ownership.ts') + await sql`UPDATE workspace_files SET id = id WHERE id = 'legacy'` + expect(await sql`SELECT content_updated_at::text AS revision FROM workspace_files`).toEqual([ + { revision: '2026-08-01 03:30:51.566952' }, + ]) + await sql`UPDATE workspace_files SET original_name = 'renamed.txt' WHERE id = 'legacy'` + expect(await sql`SELECT content_updated_at::text AS revision FROM workspace_files`).toEqual([ + { revision: '2026-08-01 03:30:51.566' }, + ]) + await sql`INSERT INTO workspace_files + (id, key, user_id, context, original_name, content_type, content_updated_at) + VALUES ('raw', 'raw-key', 'owner', 'copilot', 'raw.txt', 'text/plain', + '2026-08-01 03:30:51.123456')` + expect( + await sql`SELECT content_updated_at::text AS revision FROM workspace_files WHERE id = 'raw'` + ).toEqual([{ revision: '2026-08-01 03:30:51.123' }]) + }) + }) +}) diff --git a/packages/db/scripts/reconcile-file-ownership.ts b/packages/db/scripts/reconcile-file-ownership.ts index 7611693b22c..d39b3b919fd 100644 --- a/packages/db/scripts/reconcile-file-ownership.ts +++ b/packages/db/scripts/reconcile-file-ownership.ts @@ -9,6 +9,7 @@ const sql = postgres(url, { max: 1, connect_timeout: 10 }) const migrations = [ '0313_puzzling_zodiak.sql', + '0358_workspace_file_content_version_precision.sql', '0359_workspace_file_search_chunks.sql', '0403_file_entity_ownership.sql', '0404_file_folder_version_ownership.sql', From 83bdee2278be58022c9e11f475e2153dc56fab7b Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 16:57:10 -0700 Subject: [PATCH 046/113] fix(tests): restore Project enforcement fixtures after migration renumbering --- .../lib/projects/__integration__/foundation.integration.ts | 2 +- .../orchestration/create-workflow-with-state.integration.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 09ac639370a..eb179778ed1 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -119,7 +119,7 @@ async function enforce() { try { const migration = await readFile( new URL( - '../../../../../packages/db/migrations/0397_project_membership_enforcement.sql', + '../../../../../packages/db/migrations/0403_project_membership_enforcement.sql', import.meta.url ), 'utf8' diff --git a/apps/sim/lib/workflows/orchestration/create-workflow-with-state.integration.ts b/apps/sim/lib/workflows/orchestration/create-workflow-with-state.integration.ts index 9b4f9f10674..864925778ef 100644 --- a/apps/sim/lib/workflows/orchestration/create-workflow-with-state.integration.ts +++ b/apps/sim/lib/workflows/orchestration/create-workflow-with-state.integration.ts @@ -3,6 +3,7 @@ import { mkdir, writeFile } from 'node:fs/promises' import { dirname, resolve } from 'node:path' import { db } from '@sim/db' import { user, workflow, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import type { WorkflowState } from '@sim/workflow-types/workflow' @@ -60,7 +61,7 @@ describe('createWorkflowWithState against PostgreSQL', () => { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values([ + await insertWorkspaceFixture(db, [ { id: workspaceId, name: 'Import target', ownerId: userId, billedAccountUserId: userId }, { id: archivedWorkspaceId, @@ -78,7 +79,7 @@ describe('createWorkflowWithState against PostgreSQL', () => { resolve('test-results/create-workflow-with-state.json') await mkdir(dirname(reportPath), { recursive: true }) await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) - await db.delete(workspace).where(eq(workspace.ownerId, userId)) + await deleteWorkspaceFixture(db, eq(workspace.ownerId, userId)) await db.delete(user).where(eq(user.id, userId)) }) From 61ffede4351d10331c4cb748e61481dd51752611 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 17:29:18 -0700 Subject: [PATCH 047/113] fix(tests): scope migration probes and align remaining Project fixtures --- .../desktop-tool-authorization.integration.ts | 5 +- apps/sim/lib/table/row-changes.integration.ts | 14 +- .../db/migrations/meta/0403_snapshot.json | 3085 ++++------------- .../scripts/project-contract.integration.ts | 4 +- 4 files changed, 757 insertions(+), 2351 deletions(-) diff --git a/apps/sim/lib/mothership/tools/client/desktop-tool-authorization.integration.ts b/apps/sim/lib/mothership/tools/client/desktop-tool-authorization.integration.ts index e04d8728f9f..3e1afd3576f 100644 --- a/apps/sim/lib/mothership/tools/client/desktop-tool-authorization.integration.ts +++ b/apps/sim/lib/mothership/tools/client/desktop-tool-authorization.integration.ts @@ -25,6 +25,7 @@ vi.mock('@/lib/auth', () => authMock) import { db } from '@sim/db' import { copilotAsyncToolCalls, copilotChats, copilotRuns, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { eq } from 'drizzle-orm' import { NextRequest } from 'next/server' @@ -203,7 +204,7 @@ describe.runIf(Boolean(redisUrl))('desktop tool calls the server no longer admit createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Desktop authorization fixture', ownerId: userId, @@ -224,7 +225,7 @@ describe.runIf(Boolean(redisUrl))('desktop tool calls the server no longer admit afterAll(async () => { await db.delete(copilotChats).where(eq(copilotChats.id, chatId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(eq(user.id, userId)) }) diff --git a/apps/sim/lib/table/row-changes.integration.ts b/apps/sim/lib/table/row-changes.integration.ts index 4042b5ae006..7c27baa75ef 100644 --- a/apps/sim/lib/table/row-changes.integration.ts +++ b/apps/sim/lib/table/row-changes.integration.ts @@ -3,9 +3,11 @@ * moves it into the definition row without changing what a reader sees, under concurrent appends. */ import { db } from '@sim/db' -import { userTableDefinitions } from '@sim/db/schema' +import { userTableDefinitions, workspace } from '@sim/db/schema' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' +import { eq } from 'drizzle-orm' import postgres from 'postgres' import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { @@ -63,12 +65,16 @@ describe('table row-change log against real PostgreSQL', () => { beforeAll(async () => { await control`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at) VALUES (${userId}, 'Row change fixture', ${`${userId}@example.test`}, true, now(), now())` - await control`INSERT INTO workspace (id, name, owner_id, billed_account_user_id) - VALUES (${workspaceId}, 'Row change fixtures', ${userId}, ${userId})` + await insertWorkspaceFixture(db, { + id: workspaceId, + name: 'Row change fixtures', + ownerId: userId, + billedAccountUserId: userId, + }) }) afterAll(async () => { - await control`DELETE FROM workspace WHERE id = ${workspaceId}` + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await control`DELETE FROM "user" WHERE id = ${userId}` await control.end() }) diff --git a/packages/db/migrations/meta/0403_snapshot.json b/packages/db/migrations/meta/0403_snapshot.json index b802092bf62..9ce1cb6810a 100644 --- a/packages/db/migrations/meta/0403_snapshot.json +++ b/packages/db/migrations/meta/0403_snapshot.json @@ -139,13 +139,9 @@ "academy_certificate_user_id_user_id_fk": { "name": "academy_certificate_user_id_user_id_fk", "tableFrom": "academy_certificate", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -154,9 +150,7 @@ "uniqueConstraints": { "academy_certificate_certificate_number_unique": { "name": "academy_certificate_certificate_number_unique", - "columns": [ - "certificate_number" - ], + "columns": ["certificate_number"], "nullsNotDistinct": false } }, @@ -295,13 +289,9 @@ "account_user_id_user_id_fk": { "name": "account_user_id_user_id_fk", "tableFrom": "account", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -452,26 +442,18 @@ "agent_memory_turn_memory_id_memory_id_fk": { "name": "agent_memory_turn_memory_id_memory_id_fk", "tableFrom": "agent_memory_turn", - "columnsFrom": [ - "memory_id" - ], + "columnsFrom": ["memory_id"], "tableTo": "memory", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "agent_memory_turn_workflow_id_workflow_id_fk": { "name": "agent_memory_turn_workflow_id_workflow_id_fk", "tableFrom": "agent_memory_turn", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -625,39 +607,27 @@ "api_key_user_id_user_id_fk": { "name": "api_key_user_id_user_id_fk", "tableFrom": "api_key", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "api_key_workspace_id_workspace_id_fk": { "name": "api_key_workspace_id_workspace_id_fk", "tableFrom": "api_key", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "api_key_created_by_user_id_fk": { "name": "api_key_created_by_user_id_fk", "tableFrom": "api_key", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -666,9 +636,7 @@ "uniqueConstraints": { "api_key_key_unique": { "name": "api_key_key_unique", - "columns": [ - "key" - ], + "columns": ["key"], "nullsNotDistinct": false } }, @@ -1107,26 +1075,18 @@ "audit_log_workspace_id_workspace_id_fk": { "name": "audit_log_workspace_id_workspace_id_fk", "tableFrom": "audit_log", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "audit_log_actor_id_user_id_fk": { "name": "audit_log_actor_id_user_id_fk", "tableFrom": "audit_log", - "columnsFrom": [ - "actor_id" - ], + "columnsFrom": ["actor_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -1290,26 +1250,18 @@ "background_work_status_workspace_id_workspace_id_fk": { "name": "background_work_status_workspace_id_workspace_id_fk", "tableFrom": "background_work_status", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "background_work_status_workflow_id_workflow_id_fk": { "name": "background_work_status_workflow_id_workflow_id_fk", "tableFrom": "background_work_status", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -1494,26 +1446,18 @@ "chat_workflow_id_workflow_id_fk": { "name": "chat_workflow_id_workflow_id_fk", "tableFrom": "chat", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "chat_user_id_user_id_fk": { "name": "chat_user_id_user_id_fk", "tableFrom": "chat", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -1864,26 +1808,18 @@ "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", - "columnsFrom": [ - "run_id" - ], + "columnsFrom": ["run_id"], "tableTo": "copilot_runs", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "checkpoint_id" - ], + "columnsFrom": ["checkpoint_id"], "tableTo": "copilot_run_checkpoints", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -2255,52 +2191,36 @@ "copilot_chats_user_id_user_id_fk": { "name": "copilot_chats_user_id_user_id_fk", "tableFrom": "copilot_chats", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_chats_workflow_id_workflow_id_fk": { "name": "copilot_chats_workflow_id_workflow_id_fk", "tableFrom": "copilot_chats", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_chats_workspace_id_workspace_id_fk": { "name": "copilot_chats_workspace_id_workspace_id_fk", "tableFrom": "copilot_chats", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_chats_organization_id_organization_id_fk": { "name": "copilot_chats_organization_id_organization_id_fk", "tableFrom": "copilot_chats", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -2475,26 +2395,18 @@ "copilot_feedback_user_id_user_id_fk": { "name": "copilot_feedback_user_id_user_id_fk", "tableFrom": "copilot_feedback", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_feedback_chat_id_copilot_chats_id_fk": { "name": "copilot_feedback_chat_id_copilot_chats_id_fk", "tableFrom": "copilot_feedback", - "columnsFrom": [ - "chat_id" - ], + "columnsFrom": ["chat_id"], "tableTo": "copilot_chats", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -2724,13 +2636,9 @@ "copilot_messages_chat_id_copilot_chats_id_fk": { "name": "copilot_messages_chat_id_copilot_chats_id_fk", "tableFrom": "copilot_messages", - "columnsFrom": [ - "chat_id" - ], + "columnsFrom": ["chat_id"], "tableTo": "copilot_chats", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -2776,26 +2684,18 @@ "copilot_organization_request_stops_user_id_user_id_fk": { "name": "copilot_organization_request_stops_user_id_user_id_fk", "tableFrom": "copilot_organization_request_stops", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_organization_request_stops_organization_id_organization_id_fk": { "name": "copilot_organization_request_stops_organization_id_organization_id_fk", "tableFrom": "copilot_organization_request_stops", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -2803,11 +2703,7 @@ "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" - ] + "columns": ["user_id", "organization_id", "stream_id"] } }, "uniqueConstraints": {}, @@ -2850,26 +2746,18 @@ "copilot_request_stops_user_id_user_id_fk": { "name": "copilot_request_stops_user_id_user_id_fk", "tableFrom": "copilot_request_stops", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_request_stops_workspace_id_workspace_id_fk": { "name": "copilot_request_stops_workspace_id_workspace_id_fk", "tableFrom": "copilot_request_stops", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -2877,11 +2765,7 @@ "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" - ] + "columns": ["user_id", "workspace_id", "stream_id"] } }, "uniqueConstraints": {}, @@ -3005,13 +2889,9 @@ "copilot_run_checkpoints_run_id_copilot_runs_id_fk": { "name": "copilot_run_checkpoints_run_id_copilot_runs_id_fk", "tableFrom": "copilot_run_checkpoints", - "columnsFrom": [ - "run_id" - ], + "columnsFrom": ["run_id"], "tableTo": "copilot_runs", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -3390,78 +3270,54 @@ "copilot_runs_chat_id_copilot_chats_id_fk": { "name": "copilot_runs_chat_id_copilot_chats_id_fk", "tableFrom": "copilot_runs", - "columnsFrom": [ - "chat_id" - ], + "columnsFrom": ["chat_id"], "tableTo": "copilot_chats", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_runs_user_id_user_id_fk": { "name": "copilot_runs_user_id_user_id_fk", "tableFrom": "copilot_runs", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_runs_workflow_id_workflow_id_fk": { "name": "copilot_runs_workflow_id_workflow_id_fk", "tableFrom": "copilot_runs", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_runs_workspace_id_workspace_id_fk": { "name": "copilot_runs_workspace_id_workspace_id_fk", "tableFrom": "copilot_runs", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_runs_organization_id_organization_id_fk": { "name": "copilot_runs_organization_id_organization_id_fk", "tableFrom": "copilot_runs", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_runs_desktop_device_id_desktop_devices_id_fk": { "name": "copilot_runs_desktop_device_id_desktop_devices_id_fk", "tableFrom": "copilot_runs", - "columnsFrom": [ - "desktop_device_id" - ], + "columnsFrom": ["desktop_device_id"], "tableTo": "desktop_devices", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -3656,39 +3512,27 @@ "copilot_task_subscriptions_chat_id_copilot_chats_id_fk": { "name": "copilot_task_subscriptions_chat_id_copilot_chats_id_fk", "tableFrom": "copilot_task_subscriptions", - "columnsFrom": [ - "chat_id" - ], + "columnsFrom": ["chat_id"], "tableTo": "copilot_chats", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_task_subscriptions_workspace_id_workspace_id_fk": { "name": "copilot_task_subscriptions_workspace_id_workspace_id_fk", "tableFrom": "copilot_task_subscriptions", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_task_subscriptions_user_id_user_id_fk": { "name": "copilot_task_subscriptions_user_id_user_id_fk", "tableFrom": "copilot_task_subscriptions", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -3800,26 +3644,18 @@ "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", - "columnsFrom": [ - "chat_id" - ], + "columnsFrom": ["chat_id"], "tableTo": "copilot_chats", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "copilot_workflow_read_hashes_workflow_id_workflow_id_fk": { "name": "copilot_workflow_read_hashes_workflow_id_workflow_id_fk", "tableFrom": "copilot_workflow_read_hashes", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -4412,104 +4248,72 @@ "credential_workspace_id_workspace_id_fk": { "name": "credential_workspace_id_workspace_id_fk", "tableFrom": "credential", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_organization_id_organization_id_fk": { "name": "credential_organization_id_organization_id_fk", "tableFrom": "credential", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_slack_app_id_slack_app_id_fk": { "name": "credential_slack_app_id_slack_app_id_fk", "tableFrom": "credential", - "columnsFrom": [ - "slack_app_id" - ], + "columnsFrom": ["slack_app_id"], "tableTo": "slack_app", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "no action" }, "credential_account_id_account_id_fk": { "name": "credential_account_id_account_id_fk", "tableFrom": "credential", - "columnsFrom": [ - "account_id" - ], + "columnsFrom": ["account_id"], "tableTo": "account", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_env_owner_user_id_user_id_fk": { "name": "credential_env_owner_user_id_user_id_fk", "tableFrom": "credential", - "columnsFrom": [ - "env_owner_user_id" - ], + "columnsFrom": ["env_owner_user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_credential_group_enrollment_id_credential_group_enrollment_id_fk": { "name": "credential_credential_group_enrollment_id_credential_group_enrollment_id_fk", "tableFrom": "credential", - "columnsFrom": [ - "credential_group_enrollment_id" - ], + "columnsFrom": ["credential_group_enrollment_id"], "tableTo": "credential_group_enrollment", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_mcp_server_id_mcp_servers_id_fk": { "name": "credential_mcp_server_id_mcp_servers_id_fk", "tableFrom": "credential", - "columnsFrom": [ - "mcp_server_id" - ], + "columnsFrom": ["mcp_server_id"], "tableTo": "mcp_servers", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_created_by_user_id_fk": { "name": "credential_created_by_user_id_fk", "tableFrom": "credential", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -4708,39 +4512,27 @@ "credential_group_workspace_id_workspace_id_fk": { "name": "credential_group_workspace_id_workspace_id_fk", "tableFrom": "credential_group", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_group_organization_id_organization_id_fk": { "name": "credential_group_organization_id_organization_id_fk", "tableFrom": "credential_group", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_group_created_by_user_id_fk": { "name": "credential_group_created_by_user_id_fk", "tableFrom": "credential_group", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -4982,39 +4774,27 @@ "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", - "columnsFrom": [ - "credential_group_id" - ], + "columnsFrom": ["credential_group_id"], "tableTo": "credential_group", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_group_enrollment_user_id_user_id_fk": { "name": "credential_group_enrollment_user_id_user_id_fk", "tableFrom": "credential_group_enrollment", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_group_enrollment_created_by_user_id_fk": { "name": "credential_group_enrollment_created_by_user_id_fk", "tableFrom": "credential_group_enrollment", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -5171,39 +4951,27 @@ "credential_member_credential_id_credential_id_fk": { "name": "credential_member_credential_id_credential_id_fk", "tableFrom": "credential_member", - "columnsFrom": [ - "credential_id" - ], + "columnsFrom": ["credential_id"], "tableTo": "credential", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_member_user_id_user_id_fk": { "name": "credential_member_user_id_user_id_fk", "tableFrom": "credential_member", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "credential_member_invited_by_user_id_fk": { "name": "credential_member_invited_by_user_id_fk", "tableFrom": "credential_member", - "columnsFrom": [ - "invited_by" - ], + "columnsFrom": ["invited_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -5365,39 +5133,27 @@ "custom_block_organization_id_organization_id_fk": { "name": "custom_block_organization_id_organization_id_fk", "tableFrom": "custom_block", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "custom_block_workflow_id_workflow_id_fk": { "name": "custom_block_workflow_id_workflow_id_fk", "tableFrom": "custom_block", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "custom_block_created_by_user_id_fk": { "name": "custom_block_created_by_user_id_fk", "tableFrom": "custom_block", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -5505,26 +5261,18 @@ "custom_tools_workspace_id_workspace_id_fk": { "name": "custom_tools_workspace_id_workspace_id_fk", "tableFrom": "custom_tools", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "custom_tools_user_id_user_id_fk": { "name": "custom_tools_user_id_user_id_fk", "tableFrom": "custom_tools", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -5612,39 +5360,27 @@ "dashboard_workspace_id_workspace_id_fk": { "name": "dashboard_workspace_id_workspace_id_fk", "tableFrom": "dashboard", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "dashboard_created_by_user_id_fk": { "name": "dashboard_created_by_user_id_fk", "tableFrom": "dashboard", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "dashboard_updated_by_user_id_fk": { "name": "dashboard_updated_by_user_id_fk", "tableFrom": "dashboard", - "columnsFrom": [ - "updated_by" - ], + "columnsFrom": ["updated_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -5765,13 +5501,9 @@ "data_drain_runs_drain_id_data_drains_id_fk": { "name": "data_drain_runs_drain_id_data_drains_id_fk", "tableFrom": "data_drain_runs", - "columnsFrom": [ - "drain_id" - ], + "columnsFrom": ["drain_id"], "tableTo": "data_drains", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -5946,26 +5678,18 @@ "data_drains_organization_id_organization_id_fk": { "name": "data_drains_organization_id_organization_id_fk", "tableFrom": "data_drains", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "data_drains_created_by_user_id_fk": { "name": "data_drains_created_by_user_id_fk", "tableFrom": "data_drains", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "no action" } @@ -6086,26 +5810,18 @@ "desktop_devices_user_id_user_id_fk": { "name": "desktop_devices_user_id_user_id_fk", "tableFrom": "desktop_devices", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "desktop_devices_session_id_session_id_fk": { "name": "desktop_devices_session_id_session_id_fk", "tableFrom": "desktop_devices", - "columnsFrom": [ - "session_id" - ], + "columnsFrom": ["session_id"], "tableTo": "session", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -7343,39 +7059,27 @@ "document_knowledge_base_id_knowledge_base_id_fk": { "name": "document_knowledge_base_id_knowledge_base_id_fk", "tableFrom": "document", - "columnsFrom": [ - "knowledge_base_id" - ], + "columnsFrom": ["knowledge_base_id"], "tableTo": "knowledge_base", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "document_connector_id_knowledge_connector_id_fk": { "name": "document_connector_id_knowledge_connector_id_fk", "tableFrom": "document", - "columnsFrom": [ - "connector_id" - ], + "columnsFrom": ["connector_id"], "tableTo": "knowledge_connector", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "document_uploaded_by_user_id_fk": { "name": "document_uploaded_by_user_id_fk", "tableFrom": "document", - "columnsFrom": [ - "uploaded_by" - ], + "columnsFrom": ["uploaded_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -7433,13 +7137,9 @@ "document_secret_provenance_document_id_document_id_fk": { "name": "document_secret_provenance_document_id_document_id_fk", "tableFrom": "document_secret_provenance", - "columnsFrom": [ - "document_id" - ], + "columnsFrom": ["document_id"], "tableTo": "document", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -8098,26 +7798,18 @@ "embedding_knowledge_base_id_knowledge_base_id_fk": { "name": "embedding_knowledge_base_id_knowledge_base_id_fk", "tableFrom": "embedding", - "columnsFrom": [ - "knowledge_base_id" - ], + "columnsFrom": ["knowledge_base_id"], "tableTo": "knowledge_base", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "embedding_document_id_document_id_fk": { "name": "embedding_document_id_document_id_fk", "tableFrom": "embedding", - "columnsFrom": [ - "document_id" - ], + "columnsFrom": ["document_id"], "tableTo": "document", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -8201,13 +7893,9 @@ "embedding_keyword_tin_id_embedding_id_fk": { "name": "embedding_keyword_tin_id_embedding_id_fk", "tableFrom": "embedding_keyword_tin", - "columnsFrom": [ - "id" - ], + "columnsFrom": ["id"], "tableTo": "embedding", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -8488,13 +8176,9 @@ "embedding_search_id_embedding_id_fk": { "name": "embedding_search_id_embedding_id_fk", "tableFrom": "embedding_search", - "columnsFrom": [ - "id" - ], + "columnsFrom": ["id"], "tableTo": "embedding", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -8552,13 +8236,9 @@ "embedding_secret_provenance_embedding_id_embedding_id_fk": { "name": "embedding_secret_provenance_embedding_id_embedding_id_fk", "tableFrom": "embedding_secret_provenance", - "columnsFrom": [ - "embedding_id" - ], + "columnsFrom": ["embedding_id"], "tableTo": "embedding", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -8609,13 +8289,9 @@ "environment_user_id_user_id_fk": { "name": "environment_user_id_user_id_fk", "tableFrom": "environment", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -8624,9 +8300,7 @@ "uniqueConstraints": { "environment_user_id_unique": { "name": "environment_user_id_unique", - "columns": [ - "user_id" - ], + "columns": ["user_id"], "nullsNotDistinct": false } }, @@ -8712,13 +8386,9 @@ "execution_large_value_dependencies_workspace_id_workspace_id_fk": { "name": "execution_large_value_dependencies_workspace_id_workspace_id_fk", "tableFrom": "execution_large_value_dependencies", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -8726,10 +8396,7 @@ "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" - ] + "columns": ["parent_key", "child_key"] } }, "uniqueConstraints": {}, @@ -8828,26 +8495,18 @@ "execution_large_value_references_workspace_id_workspace_id_fk": { "name": "execution_large_value_references_workspace_id_workspace_id_fk", "tableFrom": "execution_large_value_references", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "execution_large_value_references_workflow_id_workflow_id_fk": { "name": "execution_large_value_references_workflow_id_workflow_id_fk", "tableFrom": "execution_large_value_references", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -8855,11 +8514,7 @@ "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" - ] + "columns": ["key", "execution_id", "source"] } }, "uniqueConstraints": {}, @@ -9007,26 +8662,18 @@ "execution_large_values_workspace_id_workspace_id_fk": { "name": "execution_large_values_workspace_id_workspace_id_fk", "tableFrom": "execution_large_values", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "execution_large_values_workflow_id_workflow_id_fk": { "name": "execution_large_values_workflow_id_workflow_id_fk", "tableFrom": "execution_large_values", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -9253,39 +8900,27 @@ "folder_user_id_user_id_fk": { "name": "folder_user_id_user_id_fk", "tableFrom": "folder", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "folder_workspace_id_workspace_id_fk": { "name": "folder_workspace_id_workspace_id_fk", "tableFrom": "folder", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "folder_parent_id_folder_id_fk": { "name": "folder_parent_id_folder_id_fk", "tableFrom": "folder", - "columnsFrom": [ - "parent_id" - ], + "columnsFrom": ["parent_id"], "tableTo": "folder", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -9502,26 +9137,18 @@ "invitation_inviter_id_user_id_fk": { "name": "invitation_inviter_id_user_id_fk", "tableFrom": "invitation", - "columnsFrom": [ - "inviter_id" - ], + "columnsFrom": ["inviter_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "invitation_organization_id_organization_id_fk": { "name": "invitation_organization_id_organization_id_fk", "tableFrom": "invitation", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -9530,9 +9157,7 @@ "uniqueConstraints": { "invitation_token_unique": { "name": "invitation_token_unique", - "columns": [ - "token" - ], + "columns": ["token"], "nullsNotDistinct": false } }, @@ -9626,26 +9251,18 @@ "invitation_workspace_grant_invitation_id_invitation_id_fk": { "name": "invitation_workspace_grant_invitation_id_invitation_id_fk", "tableFrom": "invitation_workspace_grant", - "columnsFrom": [ - "invitation_id" - ], + "columnsFrom": ["invitation_id"], "tableTo": "invitation", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "invitation_workspace_grant_workspace_id_workspace_id_fk": { "name": "invitation_workspace_grant_workspace_id_workspace_id_fk", "tableFrom": "invitation_workspace_grant", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -9841,26 +9458,18 @@ "job_execution_logs_schedule_id_workflow_schedule_id_fk": { "name": "job_execution_logs_schedule_id_workflow_schedule_id_fk", "tableFrom": "job_execution_logs", - "columnsFrom": [ - "schedule_id" - ], + "columnsFrom": ["schedule_id"], "tableTo": "workflow_schedule", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "job_execution_logs_workspace_id_workspace_id_fk": { "name": "job_execution_logs_workspace_id_workspace_id_fk", "tableFrom": "job_execution_logs", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -10173,52 +9782,36 @@ "knowledge_base_user_id_user_id_fk": { "name": "knowledge_base_user_id_user_id_fk", "tableFrom": "knowledge_base", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "knowledge_base_workspace_id_workspace_id_fk": { "name": "knowledge_base_workspace_id_workspace_id_fk", "tableFrom": "knowledge_base", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "knowledge_base_organization_id_organization_id_fk": { "name": "knowledge_base_organization_id_organization_id_fk", "tableFrom": "knowledge_base", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "knowledge_base_folder_id_folder_id_fk": { "name": "knowledge_base_folder_id_folder_id_fk", "tableFrom": "knowledge_base", - "columnsFrom": [ - "folder_id" - ], + "columnsFrom": ["folder_id"], "tableTo": "folder", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -10355,13 +9948,9 @@ "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", - "columnsFrom": [ - "knowledge_base_id" - ], + "columnsFrom": ["knowledge_base_id"], "tableTo": "knowledge_base", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -10735,26 +10324,18 @@ "knowledge_connector_knowledge_base_id_knowledge_base_id_fk": { "name": "knowledge_connector_knowledge_base_id_knowledge_base_id_fk", "tableFrom": "knowledge_connector", - "columnsFrom": [ - "knowledge_base_id" - ], + "columnsFrom": ["knowledge_base_id"], "tableTo": "knowledge_base", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "knowledge_connector_credential_group_id_credential_group_id_fk": { "name": "knowledge_connector_credential_group_id_credential_group_id_fk", "tableFrom": "knowledge_connector", - "columnsFrom": [ - "credential_group_id" - ], + "columnsFrom": ["credential_group_id"], "tableTo": "credential_group", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -11003,52 +10584,36 @@ "knowledge_connector_member_workspace_id_workspace_id_fk": { "name": "knowledge_connector_member_workspace_id_workspace_id_fk", "tableFrom": "knowledge_connector_member", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "knowledge_connector_member_organization_id_organization_id_fk": { "name": "knowledge_connector_member_organization_id_organization_id_fk", "tableFrom": "knowledge_connector_member", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "knowledge_connector_member_connector_id_knowledge_connector_id_fk": { "name": "knowledge_connector_member_connector_id_knowledge_connector_id_fk", "tableFrom": "knowledge_connector_member", - "columnsFrom": [ - "connector_id" - ], + "columnsFrom": ["connector_id"], "tableTo": "knowledge_connector", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "knowledge_connector_member_credential_id_credential_id_fk": { "name": "knowledge_connector_member_credential_id_credential_id_fk", "tableFrom": "knowledge_connector_member", - "columnsFrom": [ - "credential_id" - ], + "columnsFrom": ["credential_id"], "tableTo": "credential", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -11287,13 +10852,9 @@ "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", - "columnsFrom": [ - "connector_id" - ], + "columnsFrom": ["connector_id"], "tableTo": "knowledge_connector", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -11496,13 +11057,9 @@ "knowledge_connector_partition_connector_id_knowledge_connector_id_fk": { "name": "knowledge_connector_partition_connector_id_knowledge_connector_id_fk", "tableFrom": "knowledge_connector_partition", - "columnsFrom": [ - "connector_id" - ], + "columnsFrom": ["connector_id"], "tableTo": "knowledge_connector", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -11510,10 +11067,7 @@ "compositePrimaryKeys": { "kcp_pk": { "name": "kcp_pk", - "columns": [ - "connector_id", - "partition_key" - ] + "columns": ["connector_id", "partition_key"] } }, "uniqueConstraints": {}, @@ -11598,13 +11152,9 @@ "kcpg_snapshot_fk": { "name": "kcpg_snapshot_fk", "tableFrom": "knowledge_connector_permission_grant", - "columnsFrom": [ - "connector_id" - ], + "columnsFrom": ["connector_id"], "tableTo": "knowledge_connector_permission_snapshot", - "columnsTo": [ - "connector_id" - ], + "columnsTo": ["connector_id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -11612,11 +11162,7 @@ "compositePrimaryKeys": { "kcpg_pk": { "name": "kcpg_pk", - "columns": [ - "connector_id", - "group_key", - "subject_token" - ] + "columns": ["connector_id", "group_key", "subject_token"] } }, "uniqueConstraints": {}, @@ -11667,13 +11213,9 @@ "kcps_connector_fk": { "name": "kcps_connector_fk", "tableFrom": "knowledge_connector_permission_snapshot", - "columnsFrom": [ - "connector_id" - ], + "columnsFrom": ["connector_id"], "tableTo": "knowledge_connector", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -11828,13 +11370,9 @@ "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", - "columnsFrom": [ - "connector_id" - ], + "columnsFrom": ["connector_id"], "tableTo": "knowledge_connector", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -11901,26 +11439,18 @@ "knowledge_document_observation_document_id_document_id_fk": { "name": "knowledge_document_observation_document_id_document_id_fk", "tableFrom": "knowledge_document_observation", - "columnsFrom": [ - "document_id" - ], + "columnsFrom": ["document_id"], "tableTo": "document", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "member_id" - ], + "columnsFrom": ["member_id"], "tableTo": "knowledge_connector_member", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -11928,10 +11458,7 @@ "compositePrimaryKeys": { "knowledge_document_observation_document_id_member_id_pk": { "name": "knowledge_document_observation_document_id_member_id_pk", - "columns": [ - "document_id", - "member_id" - ] + "columns": ["document_id", "member_id"] } }, "uniqueConstraints": {}, @@ -12067,26 +11594,18 @@ "knowledge_external_directory_workspace_id_workspace_id_fk": { "name": "knowledge_external_directory_workspace_id_workspace_id_fk", "tableFrom": "knowledge_external_directory", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "knowledge_external_directory_organization_id_organization_id_fk": { "name": "knowledge_external_directory_organization_id_organization_id_fk", "tableFrom": "knowledge_external_directory", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -12292,26 +11811,18 @@ "knowledge_external_group_organization_id_organization_id_fk": { "name": "knowledge_external_group_organization_id_organization_id_fk", "tableFrom": "knowledge_external_group", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "keg_workspace_fk": { "name": "keg_workspace_fk", "tableFrom": "knowledge_external_group", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -12372,13 +11883,9 @@ "kegm_group_fk": { "name": "kegm_group_fk", "tableFrom": "knowledge_external_group_member", - "columnsFrom": [ - "group_id" - ], + "columnsFrom": ["group_id"], "tableTo": "knowledge_external_group", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -12386,10 +11893,7 @@ "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" - ] + "columns": ["group_id", "subject_token"] } }, "uniqueConstraints": {}, @@ -12450,13 +11954,9 @@ "knowledge_projection_dirty_document_id_document_id_fk": { "name": "knowledge_projection_dirty_document_id_document_id_fk", "tableFrom": "knowledge_projection_dirty", - "columnsFrom": [ - "document_id" - ], + "columnsFrom": ["document_id"], "tableTo": "document", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -12588,52 +12088,36 @@ "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", - "columnsFrom": [ - "mcp_server_id" - ], + "columnsFrom": ["mcp_server_id"], "tableTo": "mcp_servers", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "mcp_server_oauth_user_id_user_id_fk": { "name": "mcp_server_oauth_user_id_user_id_fk", "tableFrom": "mcp_server_oauth", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "mcp_server_oauth_workspace_id_workspace_id_fk": { "name": "mcp_server_oauth_workspace_id_workspace_id_fk", "tableFrom": "mcp_server_oauth", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "mcp_server_oauth_organization_id_organization_id_fk": { "name": "mcp_server_oauth_organization_id_organization_id_fk", "tableFrom": "mcp_server_oauth", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -12941,52 +12425,36 @@ "mcp_servers_workspace_id_workspace_id_fk": { "name": "mcp_servers_workspace_id_workspace_id_fk", "tableFrom": "mcp_servers", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "mcp_servers_organization_id_organization_id_fk": { "name": "mcp_servers_organization_id_organization_id_fk", "tableFrom": "mcp_servers", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "mcp_servers_credential_group_id_credential_group_id_fk": { "name": "mcp_servers_credential_group_id_credential_group_id_fk", "tableFrom": "mcp_servers", - "columnsFrom": [ - "credential_group_id" - ], + "columnsFrom": ["credential_group_id"], "tableTo": "credential_group", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "mcp_servers_created_by_user_id_fk": { "name": "mcp_servers_created_by_user_id_fk", "tableFrom": "mcp_servers", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -13086,26 +12554,18 @@ "member_user_id_user_id_fk": { "name": "member_user_id_user_id_fk", "tableFrom": "member", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "member_organization_id_organization_id_fk": { "name": "member_organization_id_organization_id_fk", "tableFrom": "member", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -13263,13 +12723,9 @@ "memory_workspace_id_workspace_id_fk": { "name": "memory_workspace_id_workspace_id_fk", "tableFrom": "memory", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -13318,26 +12774,18 @@ "memory_artifact_memory_id_memory_id_fk": { "name": "memory_artifact_memory_id_memory_id_fk", "tableFrom": "memory_artifact", - "columnsFrom": [ - "memory_id" - ], + "columnsFrom": ["memory_id"], "tableTo": "memory", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "memory_artifact_key_execution_large_values_key_fk": { "name": "memory_artifact_key_execution_large_values_key_fk", "tableFrom": "memory_artifact", - "columnsFrom": [ - "key" - ], + "columnsFrom": ["key"], "tableTo": "execution_large_values", - "columnsTo": [ - "key" - ], + "columnsTo": ["key"], "onUpdate": "no action", "onDelete": "cascade" } @@ -13345,10 +12793,7 @@ "compositePrimaryKeys": { "memory_artifact_memory_id_key_pk": { "name": "memory_artifact_memory_id_key_pk", - "columns": [ - "memory_id", - "key" - ] + "columns": ["memory_id", "key"] } }, "uniqueConstraints": {}, @@ -13488,13 +12933,9 @@ "memory_item_memory_id_memory_id_fk": { "name": "memory_item_memory_id_memory_id_fk", "tableFrom": "memory_item", - "columnsFrom": [ - "memory_id" - ], + "columnsFrom": ["memory_id"], "tableTo": "memory", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -13556,13 +12997,9 @@ "memory_secret_provenance_memory_id_memory_id_fk": { "name": "memory_secret_provenance_memory_id_memory_id_fk", "tableFrom": "memory_secret_provenance", - "columnsFrom": [ - "memory_id" - ], + "columnsFrom": ["memory_id"], "tableTo": "memory", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -13647,26 +13084,18 @@ "mothership_inbox_allowed_sender_workspace_id_workspace_id_fk": { "name": "mothership_inbox_allowed_sender_workspace_id_workspace_id_fk", "tableFrom": "mothership_inbox_allowed_sender", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "mothership_inbox_allowed_sender_added_by_user_id_fk": { "name": "mothership_inbox_allowed_sender_added_by_user_id_fk", "tableFrom": "mothership_inbox_allowed_sender", - "columnsFrom": [ - "added_by" - ], + "columnsFrom": ["added_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -13901,26 +13330,18 @@ "mothership_inbox_task_workspace_id_workspace_id_fk": { "name": "mothership_inbox_task_workspace_id_workspace_id_fk", "tableFrom": "mothership_inbox_task", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "mothership_inbox_task_chat_id_copilot_chats_id_fk": { "name": "mothership_inbox_task_chat_id_copilot_chats_id_fk", "tableFrom": "mothership_inbox_task", - "columnsFrom": [ - "chat_id" - ], + "columnsFrom": ["chat_id"], "tableTo": "copilot_chats", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -13972,13 +13393,9 @@ "mothership_inbox_webhook_workspace_id_workspace_id_fk": { "name": "mothership_inbox_webhook_workspace_id_workspace_id_fk", "tableFrom": "mothership_inbox_webhook", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -13987,9 +13404,7 @@ "uniqueConstraints": { "mothership_inbox_webhook_workspace_id_unique": { "name": "mothership_inbox_webhook_workspace_id_unique", - "columns": [ - "workspace_id" - ], + "columns": ["workspace_id"], "nullsNotDistinct": false } }, @@ -14026,13 +13441,9 @@ "mothership_resource_effects_chat_id_copilot_chats_id_fk": { "name": "mothership_resource_effects_chat_id_copilot_chats_id_fk", "tableFrom": "mothership_resource_effects", - "columnsFrom": [ - "chat_id" - ], + "columnsFrom": ["chat_id"], "tableTo": "copilot_chats", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -14040,10 +13451,7 @@ "compositePrimaryKeys": { "mothership_resource_effects_chat_id_effect_id_pk": { "name": "mothership_resource_effects_chat_id_effect_id_pk", - "columns": [ - "chat_id", - "effect_id" - ] + "columns": ["chat_id", "effect_id"] } }, "uniqueConstraints": {}, @@ -14102,13 +13510,9 @@ "mothership_settings_workspace_id_workspace_id_fk": { "name": "mothership_settings_workspace_id_workspace_id_fk", "tableFrom": "mothership_settings", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -14277,52 +13681,36 @@ "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", - "columnsFrom": [ - "client_id" - ], + "columnsFrom": ["client_id"], "tableTo": "oauth_client", - "columnsTo": [ - "client_id" - ], + "columnsTo": ["client_id"], "onUpdate": "no action", "onDelete": "cascade" }, "oauth_access_token_session_id_session_id_fk": { "name": "oauth_access_token_session_id_session_id_fk", "tableFrom": "oauth_access_token", - "columnsFrom": [ - "session_id" - ], + "columnsFrom": ["session_id"], "tableTo": "session", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "oauth_access_token_user_id_user_id_fk": { "name": "oauth_access_token_user_id_user_id_fk", "tableFrom": "oauth_access_token", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "refresh_id" - ], + "columnsFrom": ["refresh_id"], "tableTo": "oauth_refresh_token", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -14331,9 +13719,7 @@ "uniqueConstraints": { "oauth_access_token_token_unique": { "name": "oauth_access_token_token_unique", - "columns": [ - "token" - ], + "columns": ["token"], "nullsNotDistinct": false } }, @@ -14553,13 +13939,9 @@ "oauth_client_user_id_user_id_fk": { "name": "oauth_client_user_id_user_id_fk", "tableFrom": "oauth_client", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -14568,9 +13950,7 @@ "uniqueConstraints": { "oauth_client_client_id_unique": { "name": "oauth_client_client_id_unique", - "columns": [ - "client_id" - ], + "columns": ["client_id"], "nullsNotDistinct": false } }, @@ -14646,26 +14026,18 @@ "oauth_consent_client_id_oauth_client_client_id_fk": { "name": "oauth_consent_client_id_oauth_client_client_id_fk", "tableFrom": "oauth_consent", - "columnsFrom": [ - "client_id" - ], + "columnsFrom": ["client_id"], "tableTo": "oauth_client", - "columnsTo": [ - "client_id" - ], + "columnsTo": ["client_id"], "onUpdate": "no action", "onDelete": "cascade" }, "oauth_consent_user_id_user_id_fk": { "name": "oauth_consent_user_id_user_id_fk", "tableFrom": "oauth_consent", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -14674,11 +14046,7 @@ "uniqueConstraints": { "oauth_consent_user_client_reference_unique": { "name": "oauth_consent_user_client_reference_unique", - "columns": [ - "user_id", - "client_id", - "reference_id" - ], + "columns": ["user_id", "client_id", "reference_id"], "nullsNotDistinct": true } }, @@ -14847,52 +14215,36 @@ "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", - "columnsFrom": [ - "client_id" - ], + "columnsFrom": ["client_id"], "tableTo": "oauth_client", - "columnsTo": [ - "client_id" - ], + "columnsTo": ["client_id"], "onUpdate": "no action", "onDelete": "cascade" }, "oauth_refresh_token_session_id_session_id_fk": { "name": "oauth_refresh_token_session_id_session_id_fk", "tableFrom": "oauth_refresh_token", - "columnsFrom": [ - "session_id" - ], + "columnsFrom": ["session_id"], "tableTo": "session", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "oauth_refresh_token_user_id_user_id_fk": { "name": "oauth_refresh_token_user_id_user_id_fk", "tableFrom": "oauth_refresh_token", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "family_id" - ], + "columnsFrom": ["family_id"], "tableTo": "oauth_token_family", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -14901,17 +14253,12 @@ "uniqueConstraints": { "oauth_refresh_token_token_unique": { "name": "oauth_refresh_token_token_unique", - "columns": [ - "token" - ], + "columns": ["token"], "nullsNotDistinct": false }, "oauth_refresh_token_family_generation_unique": { "name": "oauth_refresh_token_family_generation_unique", - "columns": [ - "family_id", - "generation" - ], + "columns": ["family_id", "generation"], "nullsNotDistinct": false } }, @@ -15075,52 +14422,36 @@ "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", - "columnsFrom": [ - "client_id" - ], + "columnsFrom": ["client_id"], "tableTo": "oauth_client", - "columnsTo": [ - "client_id" - ], + "columnsTo": ["client_id"], "onUpdate": "no action", "onDelete": "cascade" }, "oauth_token_family_session_id_session_id_fk": { "name": "oauth_token_family_session_id_session_id_fk", "tableFrom": "oauth_token_family", - "columnsFrom": [ - "session_id" - ], + "columnsFrom": ["session_id"], "tableTo": "session", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "oauth_token_family_user_id_user_id_fk": { "name": "oauth_token_family_user_id_user_id_fk", "tableFrom": "oauth_token_family", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "oauth_token_family_consent_id_oauth_consent_id_fk": { "name": "oauth_token_family_consent_id_oauth_consent_id_fk", "tableFrom": "oauth_token_family", - "columnsFrom": [ - "consent_id" - ], + "columnsFrom": ["consent_id"], "tableTo": "oauth_consent", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -15288,26 +14619,18 @@ "organization_access_request_settings_organization_id_organization_id_fk": { "name": "organization_access_request_settings_organization_id_organization_id_fk", "tableFrom": "organization_access_request_settings", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "organization_access_request_settings_updated_by_user_id_fk": { "name": "organization_access_request_settings_updated_by_user_id_fk", "tableFrom": "organization_access_request_settings", - "columnsFrom": [ - "updated_by" - ], + "columnsFrom": ["updated_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -15400,26 +14723,18 @@ "organization_byok_keys_organization_id_organization_id_fk": { "name": "organization_byok_keys_organization_id_organization_id_fk", "tableFrom": "organization_byok_keys", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "organization_byok_keys_created_by_user_id_fk": { "name": "organization_byok_keys_created_by_user_id_fk", "tableFrom": "organization_byok_keys", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -15521,39 +14836,27 @@ "organization_member_usage_limit_organization_id_organization_id_fk": { "name": "organization_member_usage_limit_organization_id_organization_id_fk", "tableFrom": "organization_member_usage_limit", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "organization_member_usage_limit_user_id_user_id_fk": { "name": "organization_member_usage_limit_user_id_user_id_fk", "tableFrom": "organization_member_usage_limit", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "organization_member_usage_limit_set_by_user_id_fk": { "name": "organization_member_usage_limit_set_by_user_id_fk", "tableFrom": "organization_member_usage_limit", - "columnsFrom": [ - "set_by" - ], + "columnsFrom": ["set_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -15616,26 +14919,18 @@ "organization_search_history_organization_id_organization_id_fk": { "name": "organization_search_history_organization_id_organization_id_fk", "tableFrom": "organization_search_history", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "organization_search_history_user_id_user_id_fk": { "name": "organization_search_history_user_id_user_id_fk", "tableFrom": "organization_search_history", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -15643,10 +14938,7 @@ "compositePrimaryKeys": { "organization_search_history_organization_id_user_id_pk": { "name": "organization_search_history_organization_id_user_id_pk", - "columns": [ - "organization_id", - "user_id" - ] + "columns": ["organization_id", "user_id"] } }, "uniqueConstraints": {}, @@ -15689,13 +14981,9 @@ "organization_search_integration_organization_id_organization_id_fk": { "name": "organization_search_integration_organization_id_organization_id_fk", "tableFrom": "organization_search_integration", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -15703,10 +14991,7 @@ "compositePrimaryKeys": { "organization_search_integration_organization_id_connector_type_pk": { "name": "organization_search_integration_organization_id_connector_type_pk", - "columns": [ - "organization_id", - "connector_type" - ] + "columns": ["organization_id", "connector_type"] } }, "uniqueConstraints": {}, @@ -15804,26 +15089,18 @@ "organization_search_invocation_organization_id_organization_id_fk": { "name": "organization_search_invocation_organization_id_organization_id_fk", "tableFrom": "organization_search_invocation", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "organization_search_invocation_user_id_user_id_fk": { "name": "organization_search_invocation_user_id_user_id_fk", "tableFrom": "organization_search_invocation", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -15951,26 +15228,18 @@ "org_search_mcp_invocation_org_fk": { "name": "org_search_mcp_invocation_org_fk", "tableFrom": "organization_search_mcp_invocation", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "org_search_mcp_invocation_user_fk": { "name": "org_search_mcp_invocation_user_fk", "tableFrom": "organization_search_mcp_invocation", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -16115,26 +15384,18 @@ "organization_secret_source_id_organization_secret_source_id_fk": { "name": "organization_secret_source_id_organization_secret_source_id_fk", "tableFrom": "organization_secret", - "columnsFrom": [ - "source_id" - ], + "columnsFrom": ["source_id"], "tableTo": "organization_secret_source", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "organization_secret_owner_user_id_user_id_fk": { "name": "organization_secret_owner_user_id_user_id_fk", "tableFrom": "organization_secret", - "columnsFrom": [ - "owner_user_id" - ], + "columnsFrom": ["owner_user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -16203,13 +15464,9 @@ "organization_secret_source_organization_id_organization_id_fk": { "name": "organization_secret_source_organization_id_organization_id_fk", "tableFrom": "organization_secret_source", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -16563,13 +15820,9 @@ "paused_executions_workflow_id_workflow_id_fk": { "name": "paused_executions_workflow_id_workflow_id_fk", "tableFrom": "paused_executions", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -16727,52 +15980,36 @@ "pending_credential_draft_user_id_user_id_fk": { "name": "pending_credential_draft_user_id_user_id_fk", "tableFrom": "pending_credential_draft", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "pending_credential_draft_workspace_id_workspace_id_fk": { "name": "pending_credential_draft_workspace_id_workspace_id_fk", "tableFrom": "pending_credential_draft", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "pending_credential_draft_organization_id_organization_id_fk": { "name": "pending_credential_draft_organization_id_organization_id_fk", "tableFrom": "pending_credential_draft", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "pending_credential_draft_credential_id_credential_id_fk": { "name": "pending_credential_draft_credential_id_credential_id_fk", "tableFrom": "pending_credential_draft", - "columnsFrom": [ - "credential_id" - ], + "columnsFrom": ["credential_id"], "tableTo": "credential", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -17023,39 +16260,27 @@ "permission_access_request_organization_id_organization_id_fk": { "name": "permission_access_request_organization_id_organization_id_fk", "tableFrom": "permission_access_request", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "permission_access_request_requester_id_user_id_fk": { "name": "permission_access_request_requester_id_user_id_fk", "tableFrom": "permission_access_request", - "columnsFrom": [ - "requester_id" - ], + "columnsFrom": ["requester_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "permission_access_request_decided_by_user_id_fk": { "name": "permission_access_request_decided_by_user_id_fk", "tableFrom": "permission_access_request", - "columnsFrom": [ - "decided_by" - ], + "columnsFrom": ["decided_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -17199,26 +16424,18 @@ "permission_group_organization_id_organization_id_fk": { "name": "permission_group_organization_id_organization_id_fk", "tableFrom": "permission_group", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "permission_group_created_by_user_id_fk": { "name": "permission_group_created_by_user_id_fk", "tableFrom": "permission_group", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -17334,52 +16551,36 @@ "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", - "columnsFrom": [ - "permission_group_id" - ], + "columnsFrom": ["permission_group_id"], "tableTo": "permission_group", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "permission_group_member_organization_id_organization_id_fk": { "name": "permission_group_member_organization_id_organization_id_fk", "tableFrom": "permission_group_member", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "permission_group_member_user_id_user_id_fk": { "name": "permission_group_member_user_id_user_id_fk", "tableFrom": "permission_group_member", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "permission_group_member_assigned_by_user_id_fk": { "name": "permission_group_member_assigned_by_user_id_fk", "tableFrom": "permission_group_member", - "columnsFrom": [ - "assigned_by" - ], + "columnsFrom": ["assigned_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -17468,39 +16669,27 @@ "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", - "columnsFrom": [ - "permission_group_id" - ], + "columnsFrom": ["permission_group_id"], "tableTo": "permission_group", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "permission_group_workspace_workspace_id_workspace_id_fk": { "name": "permission_group_workspace_workspace_id_workspace_id_fk", "tableFrom": "permission_group_workspace", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "permission_group_workspace_organization_id_organization_id_fk": { "name": "permission_group_workspace_organization_id_organization_id_fk", "tableFrom": "permission_group_workspace", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -17678,13 +16867,9 @@ "permissions_user_id_user_id_fk": { "name": "permissions_user_id_user_id_fk", "tableFrom": "permissions", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -17812,26 +16997,18 @@ "pinned_item_user_id_user_id_fk": { "name": "pinned_item_user_id_user_id_fk", "tableFrom": "pinned_item", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "pinned_item_workspace_id_workspace_id_fk": { "name": "pinned_item_workspace_id_workspace_id_fk", "tableFrom": "pinned_item", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -17951,26 +17128,18 @@ "project_organization_id_organization_id_fk": { "name": "project_organization_id_organization_id_fk", "tableFrom": "project", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "restrict" }, "project_owner_id_user_id_fk": { "name": "project_owner_id_user_id_fk", "tableFrom": "project", - "columnsFrom": [ - "owner_id" - ], + "columnsFrom": ["owner_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "restrict" } @@ -18031,26 +17200,18 @@ "project_workspace_project_id_project_id_fk": { "name": "project_workspace_project_id_project_id_fk", "tableFrom": "project_workspace", - "columnsFrom": [ - "project_id" - ], + "columnsFrom": ["project_id"], "tableTo": "project", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "restrict" }, "project_workspace_workspace_id_workspace_id_fk": { "name": "project_workspace_workspace_id_workspace_id_fk", "tableFrom": "project_workspace", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -18058,10 +17219,7 @@ "compositePrimaryKeys": { "project_workspace_project_id_workspace_id_pk": { "name": "project_workspace_project_id_workspace_id_pk", - "columns": [ - "project_id", - "workspace_id" - ] + "columns": ["project_id", "workspace_id"] } }, "uniqueConstraints": {}, @@ -18223,26 +17381,18 @@ "public_share_workspace_id_workspace_id_fk": { "name": "public_share_workspace_id_workspace_id_fk", "tableFrom": "public_share", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "public_share_created_by_user_id_fk": { "name": "public_share_created_by_user_id_fk", "tableFrom": "public_share", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -18434,52 +17584,36 @@ "resource_policy_workspace_id_workspace_id_fk": { "name": "resource_policy_workspace_id_workspace_id_fk", "tableFrom": "resource_policy", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "resource_policy_organization_id_organization_id_fk": { "name": "resource_policy_organization_id_organization_id_fk", "tableFrom": "resource_policy", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "resource_policy_created_by_user_id_fk": { "name": "resource_policy_created_by_user_id_fk", "tableFrom": "resource_policy", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "resource_policy_updated_by_user_id_fk": { "name": "resource_policy_updated_by_user_id_fk", "tableFrom": "resource_policy", - "columnsFrom": [ - "updated_by" - ], + "columnsFrom": ["updated_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -18616,13 +17750,9 @@ "resume_queue_paused_execution_id_paused_executions_id_fk": { "name": "resume_queue_paused_execution_id_paused_executions_id_fk", "tableFrom": "resume_queue", - "columnsFrom": [ - "paused_execution_id" - ], + "columnsFrom": ["paused_execution_id"], "tableTo": "paused_executions", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -18903,26 +18033,18 @@ "scim_connection_organization_id_organization_id_fk": { "name": "scim_connection_organization_id_organization_id_fk", "tableFrom": "scim_connection", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "scim_connection_created_by_user_id_fk": { "name": "scim_connection_created_by_user_id_fk", "tableFrom": "scim_connection", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -19041,39 +18163,27 @@ "scim_credential_connection_id_scim_connection_id_fk": { "name": "scim_credential_connection_id_scim_connection_id_fk", "tableFrom": "scim_credential", - "columnsFrom": [ - "connection_id" - ], + "columnsFrom": ["connection_id"], "tableTo": "scim_connection", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "scim_credential_revoked_by_user_id_fk": { "name": "scim_credential_revoked_by_user_id_fk", "tableFrom": "scim_credential", - "columnsFrom": [ - "revoked_by" - ], + "columnsFrom": ["revoked_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "scim_credential_created_by_user_id_fk": { "name": "scim_credential_created_by_user_id_fk", "tableFrom": "scim_credential", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -19209,13 +18319,9 @@ "scim_group_connection_id_scim_connection_id_fk": { "name": "scim_group_connection_id_scim_connection_id_fk", "tableFrom": "scim_group", - "columnsFrom": [ - "connection_id" - ], + "columnsFrom": ["connection_id"], "tableTo": "scim_connection", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -19372,52 +18478,36 @@ "scim_group_mapping_group_id_scim_group_id_fk": { "name": "scim_group_mapping_group_id_scim_group_id_fk", "tableFrom": "scim_group_mapping", - "columnsFrom": [ - "group_id" - ], + "columnsFrom": ["group_id"], "tableTo": "scim_group", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "permission_group_id" - ], + "columnsFrom": ["permission_group_id"], "tableTo": "permission_group", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "scim_group_mapping_workspace_id_workspace_id_fk": { "name": "scim_group_mapping_workspace_id_workspace_id_fk", "tableFrom": "scim_group_mapping", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "scim_group_mapping_created_by_user_id_fk": { "name": "scim_group_mapping_created_by_user_id_fk", "tableFrom": "scim_group_mapping", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -19505,26 +18595,18 @@ "scim_group_member_group_id_scim_group_id_fk": { "name": "scim_group_member_group_id_scim_group_id_fk", "tableFrom": "scim_group_member", - "columnsFrom": [ - "group_id" - ], + "columnsFrom": ["group_id"], "tableTo": "scim_group", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "scim_user_id" - ], + "columnsFrom": ["scim_user_id"], "tableTo": "scim_user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -19653,26 +18735,18 @@ "scim_projection_grant_connection_id_scim_connection_id_fk": { "name": "scim_projection_grant_connection_id_scim_connection_id_fk", "tableFrom": "scim_projection_grant", - "columnsFrom": [ - "connection_id" - ], + "columnsFrom": ["connection_id"], "tableTo": "scim_connection", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "scim_user_id" - ], + "columnsFrom": ["scim_user_id"], "tableTo": "scim_user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -19782,13 +18856,9 @@ "scim_request_log_connection_id_scim_connection_id_fk": { "name": "scim_request_log_connection_id_scim_connection_id_fk", "tableFrom": "scim_request_log", - "columnsFrom": [ - "connection_id" - ], + "columnsFrom": ["connection_id"], "tableTo": "scim_connection", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -19973,26 +19043,18 @@ "scim_user_connection_id_scim_connection_id_fk": { "name": "scim_user_connection_id_scim_connection_id_fk", "tableFrom": "scim_user", - "columnsFrom": [ - "connection_id" - ], + "columnsFrom": ["connection_id"], "tableTo": "scim_connection", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "scim_user_user_id_user_id_fk": { "name": "scim_user_user_id_user_id_fk", "tableFrom": "scim_user", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -20081,26 +19143,18 @@ "scim_user_tombstone_connection_id_scim_connection_id_fk": { "name": "scim_user_tombstone_connection_id_scim_connection_id_fk", "tableFrom": "scim_user_tombstone", - "columnsFrom": [ - "connection_id" - ], + "columnsFrom": ["connection_id"], "tableTo": "scim_connection", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "scim_user_tombstone_user_id_user_id_fk": { "name": "scim_user_tombstone_user_id_user_id_fk", "tableFrom": "scim_user_tombstone", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -20316,13 +19370,9 @@ "secret_usage_workspace_id_workspace_id_fk": { "name": "secret_usage_workspace_id_workspace_id_fk", "tableFrom": "secret_usage", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -20419,26 +19469,18 @@ "session_user_id_user_id_fk": { "name": "session_user_id_user_id_fk", "tableFrom": "session", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "session_active_organization_id_organization_id_fk": { "name": "session_active_organization_id_organization_id_fk", "tableFrom": "session", - "columnsFrom": [ - "active_organization_id" - ], + "columnsFrom": ["active_organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -20447,9 +19489,7 @@ "uniqueConstraints": { "session_token_unique": { "name": "session_token_unique", - "columns": [ - "token" - ], + "columns": ["token"], "nullsNotDistinct": false } }, @@ -20596,13 +19636,9 @@ "settings_user_id_user_id_fk": { "name": "settings_user_id_user_id_fk", "tableFrom": "settings", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -20611,9 +19647,7 @@ "uniqueConstraints": { "settings_user_id_unique": { "name": "settings_user_id_unique", - "columns": [ - "user_id" - ], + "columns": ["user_id"], "nullsNotDistinct": false } }, @@ -20663,13 +19697,9 @@ "sim_trigger_state_workflow_id_workflow_id_fk": { "name": "sim_trigger_state_workflow_id_workflow_id_fk", "tableFrom": "sim_trigger_state", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -20677,11 +19707,7 @@ "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" - ] + "columns": ["workflow_id", "block_id", "scope_key"] } }, "uniqueConstraints": {}, @@ -20771,26 +19797,18 @@ "skill_workspace_id_workspace_id_fk": { "name": "skill_workspace_id_workspace_id_fk", "tableFrom": "skill", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "skill_user_id_user_id_fk": { "name": "skill_user_id_user_id_fk", "tableFrom": "skill", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -20886,39 +19904,27 @@ "skill_member_skill_id_skill_id_fk": { "name": "skill_member_skill_id_skill_id_fk", "tableFrom": "skill_member", - "columnsFrom": [ - "skill_id" - ], + "columnsFrom": ["skill_id"], "tableTo": "skill", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "skill_member_user_id_user_id_fk": { "name": "skill_member_user_id_user_id_fk", "tableFrom": "skill_member", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "skill_member_invited_by_user_id_fk": { "name": "skill_member_invited_by_user_id_fk", "tableFrom": "skill_member", - "columnsFrom": [ - "invited_by" - ], + "columnsFrom": ["invited_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -20995,13 +20001,9 @@ "slack_app_organization_id_organization_id_fk": { "name": "slack_app_organization_id_organization_id_fk", "tableFrom": "slack_app", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -21198,39 +20200,27 @@ "slack_search_installation_organization_id_organization_id_fk": { "name": "slack_search_installation_organization_id_organization_id_fk", "tableFrom": "slack_search_installation", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "slack_search_installation_credential_id_credential_id_fk": { "name": "slack_search_installation_credential_id_credential_id_fk", "tableFrom": "slack_search_installation", - "columnsFrom": [ - "credential_id" - ], + "columnsFrom": ["credential_id"], "tableTo": "credential", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "slack_app_id" - ], + "columnsFrom": ["slack_app_id"], "tableTo": "slack_app", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "no action" } @@ -21423,13 +20413,9 @@ "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", - "columnsFrom": [ - "installation_id" - ], + "columnsFrom": ["installation_id"], "tableTo": "slack_search_installation", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -21581,26 +20567,18 @@ "sso_domain_organization_id_organization_id_fk": { "name": "sso_domain_organization_id_organization_id_fk", "tableFrom": "sso_domain", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "sso_domain_created_by_user_id_fk": { "name": "sso_domain_created_by_user_id_fk", "tableFrom": "sso_domain", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -21744,26 +20722,18 @@ "sso_provider_user_id_user_id_fk": { "name": "sso_provider_user_id_user_id_fk", "tableFrom": "sso_provider", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "sso_provider_organization_id_organization_id_fk": { "name": "sso_provider_organization_id_organization_id_fk", "tableFrom": "sso_provider", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -22083,26 +21053,18 @@ "table_jobs_table_id_user_table_definitions_id_fk": { "name": "table_jobs_table_id_user_table_definitions_id_fk", "tableFrom": "table_jobs", - "columnsFrom": [ - "table_id" - ], + "columnsFrom": ["table_id"], "tableTo": "user_table_definitions", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "table_jobs_workspace_id_workspace_id_fk": { "name": "table_jobs_workspace_id_workspace_id_fk", "tableFrom": "table_jobs", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -22270,39 +21232,27 @@ "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", - "columnsFrom": [ - "table_id" - ], + "columnsFrom": ["table_id"], "tableTo": "user_table_definitions", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "row_id" - ], + "columnsFrom": ["row_id"], "tableTo": "user_table_rows", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "capability_governed_user_id" - ], + "columnsFrom": ["capability_governed_user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -22310,10 +21260,7 @@ "compositePrimaryKeys": { "table_row_executions_row_id_group_id_pk": { "name": "table_row_executions_row_id_group_id_pk", - "columns": [ - "row_id", - "group_id" - ] + "columns": ["row_id", "group_id"] } }, "uniqueConstraints": {}, @@ -22503,52 +21450,36 @@ "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", - "columnsFrom": [ - "table_id" - ], + "columnsFrom": ["table_id"], "tableTo": "user_table_definitions", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "table_run_dispatches_workspace_id_workspace_id_fk": { "name": "table_run_dispatches_workspace_id_workspace_id_fk", "tableFrom": "table_run_dispatches", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "triggered_by_user_id" - ], + "columnsFrom": ["triggered_by_user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "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", - "columnsFrom": [ - "capability_governed_user_id" - ], + "columnsFrom": ["capability_governed_user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -22692,39 +21623,27 @@ "table_views_table_id_user_table_definitions_id_fk": { "name": "table_views_table_id_user_table_definitions_id_fk", "tableFrom": "table_views", - "columnsFrom": [ - "table_id" - ], + "columnsFrom": ["table_id"], "tableTo": "user_table_definitions", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "table_views_workspace_id_workspace_id_fk": { "name": "table_views_workspace_id_workspace_id_fk", "tableFrom": "table_views", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "table_views_created_by_user_id_fk": { "name": "table_views_created_by_user_id_fk", "tableFrom": "table_views", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -23334,39 +22253,27 @@ "usage_log_user_id_user_id_fk": { "name": "usage_log_user_id_user_id_fk", "tableFrom": "usage_log", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "usage_log_workspace_id_workspace_id_fk": { "name": "usage_log_workspace_id_workspace_id_fk", "tableFrom": "usage_log", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "usage_log_workflow_id_workflow_id_fk": { "name": "usage_log_workflow_id_workflow_id_fk", "tableFrom": "usage_log", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -23501,16 +22408,12 @@ "uniqueConstraints": { "user_email_unique": { "name": "user_email_unique", - "columns": [ - "email" - ], + "columns": ["email"], "nullsNotDistinct": false }, "user_normalized_email_unique": { "name": "user_normalized_email_unique", - "columns": [ - "normalized_email" - ], + "columns": ["normalized_email"], "nullsNotDistinct": false } }, @@ -23610,13 +22513,9 @@ "user_stats_user_id_user_id_fk": { "name": "user_stats_user_id_user_id_fk", "tableFrom": "user_stats", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -23625,9 +22524,7 @@ "uniqueConstraints": { "user_stats_user_id_unique": { "name": "user_stats_user_id_unique", - "columns": [ - "user_id" - ], + "columns": ["user_id"], "nullsNotDistinct": false } }, @@ -23852,39 +22749,27 @@ "user_table_definitions_workspace_id_workspace_id_fk": { "name": "user_table_definitions_workspace_id_workspace_id_fk", "tableFrom": "user_table_definitions", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "user_table_definitions_folder_id_folder_id_fk": { "name": "user_table_definitions_folder_id_folder_id_fk", "tableFrom": "user_table_definitions", - "columnsFrom": [ - "folder_id" - ], + "columnsFrom": ["folder_id"], "tableTo": "folder", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "user_table_definitions_created_by_user_id_fk": { "name": "user_table_definitions_created_by_user_id_fk", "tableFrom": "user_table_definitions", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -23950,13 +22835,9 @@ "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", - "columnsFrom": [ - "table_id" - ], + "columnsFrom": ["table_id"], "tableTo": "user_table_definitions", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -24009,13 +22890,9 @@ "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", - "columnsFrom": [ - "row_id" - ], + "columnsFrom": ["row_id"], "tableTo": "user_table_rows", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -24243,39 +23120,27 @@ "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", - "columnsFrom": [ - "table_id" - ], + "columnsFrom": ["table_id"], "tableTo": "user_table_definitions", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "user_table_rows_workspace_id_workspace_id_fk": { "name": "user_table_rows_workspace_id_workspace_id_fk", "tableFrom": "user_table_rows", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "user_table_rows_created_by_user_id_fk": { "name": "user_table_rows_created_by_user_id_fk", "tableFrom": "user_table_rows", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -24410,9 +23275,7 @@ "uniqueConstraints": { "waitlist_email_unique": { "name": "waitlist_email_unique", - "columns": [ - "email" - ], + "columns": ["email"], "nullsNotDistinct": false } }, @@ -24755,26 +23618,18 @@ "webhook_workflow_id_workflow_id_fk": { "name": "webhook_workflow_id_workflow_id_fk", "tableFrom": "webhook", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "webhook_deployment_version_id_workflow_deployment_version_id_fk": { "name": "webhook_deployment_version_id_workflow_deployment_version_id_fk", "tableFrom": "webhook", - "columnsFrom": [ - "deployment_version_id" - ], + "columnsFrom": ["deployment_version_id"], "tableTo": "workflow_deployment_version", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -24852,13 +23707,9 @@ "webhook_path_claim_workflow_id_workflow_id_fk": { "name": "webhook_path_claim_workflow_id_workflow_id_fk", "tableFrom": "webhook_path_claim", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -25177,39 +24028,27 @@ "workflow_user_id_user_id_fk": { "name": "workflow_user_id_user_id_fk", "tableFrom": "workflow", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_workspace_id_workspace_id_fk": { "name": "workflow_workspace_id_workspace_id_fk", "tableFrom": "workflow", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_folder_id_folder_id_fk": { "name": "workflow_folder_id_folder_id_fk", "tableFrom": "workflow", - "columnsFrom": [ - "folder_id" - ], + "columnsFrom": ["folder_id"], "tableTo": "folder", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -25394,13 +24233,9 @@ "workflow_blocks_workflow_id_workflow_id_fk": { "name": "workflow_blocks_workflow_id_workflow_id_fk", "tableFrom": "workflow_blocks", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -25611,39 +24446,27 @@ "workflow_checkpoints_user_id_user_id_fk": { "name": "workflow_checkpoints_user_id_user_id_fk", "tableFrom": "workflow_checkpoints", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_checkpoints_workflow_id_workflow_id_fk": { "name": "workflow_checkpoints_workflow_id_workflow_id_fk", "tableFrom": "workflow_checkpoints", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_checkpoints_chat_id_copilot_chats_id_fk": { "name": "workflow_checkpoints_chat_id_copilot_chats_id_fk", "tableFrom": "workflow_checkpoints", - "columnsFrom": [ - "chat_id" - ], + "columnsFrom": ["chat_id"], "tableTo": "copilot_chats", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -25899,39 +24722,27 @@ "workflow_deployment_operation_workflow_id_workflow_id_fk": { "name": "workflow_deployment_operation_workflow_id_workflow_id_fk", "tableFrom": "workflow_deployment_operation", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "deployment_version_id" - ], + "columnsFrom": ["deployment_version_id"], "tableTo": "workflow_deployment_version", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "previous_active_version_id" - ], + "columnsFrom": ["previous_active_version_id"], "tableTo": "workflow_deployment_version", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -26083,13 +24894,9 @@ "workflow_deployment_version_workflow_id_workflow_id_fk": { "name": "workflow_deployment_version_workflow_id_workflow_id_fk", "tableFrom": "workflow_deployment_version", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -26211,39 +25018,27 @@ "workflow_edges_workflow_id_workflow_id_fk": { "name": "workflow_edges_workflow_id_workflow_id_fk", "tableFrom": "workflow_edges", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_edges_source_block_id_workflow_blocks_id_fk": { "name": "workflow_edges_source_block_id_workflow_blocks_id_fk", "tableFrom": "workflow_edges", - "columnsFrom": [ - "source_block_id" - ], + "columnsFrom": ["source_block_id"], "tableTo": "workflow_blocks", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_edges_target_block_id_workflow_blocks_id_fk": { "name": "workflow_edges_target_block_id_workflow_blocks_id_fk", "tableFrom": "workflow_edges", - "columnsFrom": [ - "target_block_id" - ], + "columnsFrom": ["target_block_id"], "tableTo": "workflow_blocks", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -26750,52 +25545,36 @@ "workflow_execution_logs_workflow_id_workflow_id_fk": { "name": "workflow_execution_logs_workflow_id_workflow_id_fk", "tableFrom": "workflow_execution_logs", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "workflow_execution_logs_workspace_id_workspace_id_fk": { "name": "workflow_execution_logs_workspace_id_workspace_id_fk", "tableFrom": "workflow_execution_logs", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "state_snapshot_id" - ], + "columnsFrom": ["state_snapshot_id"], "tableTo": "workflow_execution_snapshots", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "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", - "columnsFrom": [ - "deployment_version_id" - ], + "columnsFrom": ["deployment_version_id"], "tableTo": "workflow_deployment_version", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -26914,13 +25693,9 @@ "workflow_execution_snapshots_workflow_id_workflow_id_fk": { "name": "workflow_execution_snapshots_workflow_id_workflow_id_fk", "tableFrom": "workflow_execution_snapshots", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -27066,26 +25841,18 @@ "workflow_mcp_server_workspace_id_workspace_id_fk": { "name": "workflow_mcp_server_workspace_id_workspace_id_fk", "tableFrom": "workflow_mcp_server", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_mcp_server_created_by_user_id_fk": { "name": "workflow_mcp_server_created_by_user_id_fk", "tableFrom": "workflow_mcp_server", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -27239,26 +26006,18 @@ "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", - "columnsFrom": [ - "server_id" - ], + "columnsFrom": ["server_id"], "tableTo": "workflow_mcp_server", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_mcp_tool_workflow_id_workflow_id_fk": { "name": "workflow_mcp_tool_workflow_id_workflow_id_fk", "tableFrom": "workflow_mcp_tool", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -27655,65 +26414,45 @@ "workflow_schedule_workflow_id_workflow_id_fk": { "name": "workflow_schedule_workflow_id_workflow_id_fk", "tableFrom": "workflow_schedule", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_schedule_deployment_version_id_workflow_deployment_version_id_fk": { "name": "workflow_schedule_deployment_version_id_workflow_deployment_version_id_fk", "tableFrom": "workflow_schedule", - "columnsFrom": [ - "deployment_version_id" - ], + "columnsFrom": ["deployment_version_id"], "tableTo": "workflow_deployment_version", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_schedule_deployment_operation_id_workflow_deployment_operation_id_fk": { "name": "workflow_schedule_deployment_operation_id_workflow_deployment_operation_id_fk", "tableFrom": "workflow_schedule", - "columnsFrom": [ - "deployment_operation_id" - ], + "columnsFrom": ["deployment_operation_id"], "tableTo": "workflow_deployment_operation", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "workflow_schedule_source_user_id_user_id_fk": { "name": "workflow_schedule_source_user_id_user_id_fk", "tableFrom": "workflow_schedule", - "columnsFrom": [ - "source_user_id" - ], + "columnsFrom": ["source_user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workflow_schedule_source_workspace_id_workspace_id_fk": { "name": "workflow_schedule_source_workspace_id_workspace_id_fk", "tableFrom": "workflow_schedule", - "columnsFrom": [ - "source_workspace_id" - ], + "columnsFrom": ["source_workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -27810,13 +26549,9 @@ "workflow_subflows_workflow_id_workflow_id_fk": { "name": "workflow_subflows_workflow_id_workflow_id_fk", "tableFrom": "workflow_subflows", - "columnsFrom": [ - "workflow_id" - ], + "columnsFrom": ["workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -28051,52 +26786,36 @@ "workspace_owner_id_user_id_fk": { "name": "workspace_owner_id_user_id_fk", "tableFrom": "workspace", - "columnsFrom": [ - "owner_id" - ], + "columnsFrom": ["owner_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_organization_id_organization_id_fk": { "name": "workspace_organization_id_organization_id_fk", "tableFrom": "workspace", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "workspace_billed_account_user_id_user_id_fk": { "name": "workspace_billed_account_user_id_user_id_fk", "tableFrom": "workspace", - "columnsFrom": [ - "billed_account_user_id" - ], + "columnsFrom": ["billed_account_user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "no action" }, "workspace_forked_from_workspace_id_workspace_id_fk": { "name": "workspace_forked_from_workspace_id_workspace_id_fk", "tableFrom": "workspace", - "columnsFrom": [ - "forked_from_workspace_id" - ], + "columnsFrom": ["forked_from_workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -28194,26 +26913,18 @@ "workspace_byok_keys_workspace_id_workspace_id_fk": { "name": "workspace_byok_keys_workspace_id_workspace_id_fk", "tableFrom": "workspace_byok_keys", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_byok_keys_created_by_user_id_fk": { "name": "workspace_byok_keys_created_by_user_id_fk", "tableFrom": "workspace_byok_keys", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -28283,13 +26994,9 @@ "workspace_environment_workspace_id_workspace_id_fk": { "name": "workspace_environment_workspace_id_workspace_id_fk", "tableFrom": "workspace_environment", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -28418,26 +27125,18 @@ "workspace_file_workspace_id_workspace_id_fk": { "name": "workspace_file_workspace_id_workspace_id_fk", "tableFrom": "workspace_file", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_file_uploaded_by_user_id_fk": { "name": "workspace_file_uploaded_by_user_id_fk", "tableFrom": "workspace_file", - "columnsFrom": [ - "uploaded_by" - ], + "columnsFrom": ["uploaded_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -28446,9 +27145,7 @@ "uniqueConstraints": { "workspace_file_key_unique": { "name": "workspace_file_key_unique", - "columns": [ - "key" - ], + "columns": ["key"], "nullsNotDistinct": false } }, @@ -28491,13 +27188,9 @@ "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", - "columnsFrom": [ - "file_id" - ], + "columnsFrom": ["file_id"], "tableTo": "workspace_files", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -28746,13 +27439,9 @@ "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", - "columnsFrom": [ - "build_id" - ], + "columnsFrom": ["build_id"], "tableTo": "workspace_file_search_build", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "no action" } @@ -28760,10 +27449,7 @@ "compositePrimaryKeys": { "workspace_file_search_chunk_pk": { "name": "workspace_file_search_chunk_pk", - "columns": [ - "build_id", - "ordinal" - ] + "columns": ["build_id", "ordinal"] } }, "uniqueConstraints": {}, @@ -28844,13 +27530,9 @@ "workspace_file_search_queue_workspace_fk": { "name": "workspace_file_search_queue_workspace_fk", "tableFrom": "workspace_file_search_dispatch_queue", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -29028,26 +27710,18 @@ "workspace_file_search_index_file_fk": { "name": "workspace_file_search_index_file_fk", "tableFrom": "workspace_file_search_index", - "columnsFrom": [ - "file_id" - ], + "columnsFrom": ["file_id"], "tableTo": "workspace_files", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_file_search_index_workspace_fk": { "name": "workspace_file_search_index_workspace_fk", "tableFrom": "workspace_file_search_index", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -29055,10 +27729,7 @@ "compositePrimaryKeys": { "workspace_file_search_index_pk": { "name": "workspace_file_search_index_pk", - "columns": [ - "file_id", - "source_content_updated_at" - ] + "columns": ["file_id", "source_content_updated_at"] } }, "uniqueConstraints": {}, @@ -29247,26 +27918,18 @@ "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", - "columnsFrom": [ - "file_id" - ], + "columnsFrom": ["file_id"], "tableTo": "workspace_files", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "build_id" - ], + "columnsFrom": ["build_id"], "tableTo": "workspace_file_search_build", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -29386,26 +28049,18 @@ "workspace_file_search_segment_file_fk": { "name": "workspace_file_search_segment_file_fk", "tableFrom": "workspace_file_search_segment", - "columnsFrom": [ - "file_id" - ], + "columnsFrom": ["file_id"], "tableTo": "workspace_files", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_file_search_segment_workspace_fk": { "name": "workspace_file_search_segment_workspace_fk", "tableFrom": "workspace_file_search_segment", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -29413,12 +28068,7 @@ "compositePrimaryKeys": { "workspace_file_search_segment_pk": { "name": "workspace_file_search_segment_pk", - "columns": [ - "file_id", - "source_content_updated_at", - "line_number", - "segment_number" - ] + "columns": ["file_id", "source_content_updated_at", "line_number", "segment_number"] } }, "uniqueConstraints": {}, @@ -29468,13 +28118,9 @@ "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", - "columnsFrom": [ - "file_id" - ], + "columnsFrom": ["file_id"], "tableTo": "workspace_files", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -29681,26 +28327,18 @@ "workspace_file_version_file_id_workspace_files_id_fk": { "name": "workspace_file_version_file_id_workspace_files_id_fk", "tableFrom": "workspace_file_version", - "columnsFrom": [ - "file_id" - ], + "columnsFrom": ["file_id"], "tableTo": "workspace_files", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_file_version_workspace_id_workspace_id_fk": { "name": "workspace_file_version_workspace_id_workspace_id_fk", "tableFrom": "workspace_file_version", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -30080,65 +28718,45 @@ "workspace_files_user_id_user_id_fk": { "name": "workspace_files_user_id_user_id_fk", "tableFrom": "workspace_files", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_files_workspace_id_workspace_id_fk": { "name": "workspace_files_workspace_id_workspace_id_fk", "tableFrom": "workspace_files", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_files_organization_id_organization_id_fk": { "name": "workspace_files_organization_id_organization_id_fk", "tableFrom": "workspace_files", - "columnsFrom": [ - "organization_id" - ], + "columnsFrom": ["organization_id"], "tableTo": "organization", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_files_folder_id_folder_id_fk": { "name": "workspace_files_folder_id_folder_id_fk", "tableFrom": "workspace_files", - "columnsFrom": [ - "folder_id" - ], + "columnsFrom": ["folder_id"], "tableTo": "folder", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" }, "workspace_files_chat_id_copilot_chats_id_fk": { "name": "workspace_files_chat_id_copilot_chats_id_fk", "tableFrom": "workspace_files", - "columnsFrom": [ - "chat_id" - ], + "columnsFrom": ["chat_id"], "tableTo": "copilot_chats", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -30299,13 +28917,9 @@ "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", - "columnsFrom": [ - "child_workspace_id" - ], + "columnsFrom": ["child_workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -30431,13 +29045,9 @@ "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", - "columnsFrom": [ - "child_workspace_id" - ], + "columnsFrom": ["child_workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -30545,26 +29155,18 @@ "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", - "columnsFrom": [ - "child_workspace_id" - ], + "columnsFrom": ["child_workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_fork_promote_run_created_by_user_id_fk": { "name": "workspace_fork_promote_run_created_by_user_id_fk", "tableFrom": "workspace_fork_promote_run", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -30700,26 +29302,18 @@ "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", - "columnsFrom": [ - "child_workspace_id" - ], + "columnsFrom": ["child_workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_fork_resource_map_created_by_user_id_fk": { "name": "workspace_fork_resource_map_created_by_user_id_fk", "tableFrom": "workspace_fork_resource_map", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -30928,39 +29522,27 @@ "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", - "columnsFrom": [ - "child_workspace_id" - ], + "columnsFrom": ["child_workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "source_workflow_id" - ], + "columnsFrom": ["source_workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "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", - "columnsFrom": [ - "target_workflow_id" - ], + "columnsFrom": ["target_workflow_id"], "tableTo": "workflow", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -31080,13 +29662,9 @@ "workspace_operation_receipt_workspace_id_workspace_id_fk": { "name": "workspace_operation_receipt_workspace_id_workspace_id_fk", "tableFrom": "workspace_operation_receipt", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -31231,26 +29809,18 @@ "workspace_sandbox_workspace_id_workspace_id_fk": { "name": "workspace_sandbox_workspace_id_workspace_id_fk", "tableFrom": "workspace_sandbox", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_sandbox_created_by_user_id_fk": { "name": "workspace_sandbox_created_by_user_id_fk", "tableFrom": "workspace_sandbox", - "columnsFrom": [ - "created_by" - ], + "columnsFrom": ["created_by"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "set null" } @@ -31306,26 +29876,18 @@ "workspace_visit_user_id_user_id_fk": { "name": "workspace_visit_user_id_user_id_fk", "tableFrom": "workspace_visit", - "columnsFrom": [ - "user_id" - ], + "columnsFrom": ["user_id"], "tableTo": "user", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" }, "workspace_visit_workspace_id_workspace_id_fk": { "name": "workspace_visit_workspace_id_workspace_id_fk", "tableFrom": "workspace_visit", - "columnsFrom": [ - "workspace_id" - ], + "columnsFrom": ["workspace_id"], "tableTo": "workspace", - "columnsTo": [ - "id" - ], + "columnsTo": ["id"], "onUpdate": "no action", "onDelete": "cascade" } @@ -31333,10 +29895,7 @@ "compositePrimaryKeys": { "workspace_visit_user_id_workspace_id_pk": { "name": "workspace_visit_user_id_workspace_id_pk", - "columns": [ - "user_id", - "workspace_id" - ] + "columns": ["user_id", "workspace_id"] } }, "uniqueConstraints": {}, @@ -31349,126 +29908,67 @@ "public.academy_cert_status": { "name": "academy_cert_status", "schema": "public", - "values": [ - "active", - "revoked", - "expired" - ] + "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" - ] + "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" - ] + "values": ["pending", "processing", "completed", "completed_with_warnings", "failed"] }, "public.billing_blocked_reason": { "name": "billing_blocked_reason", "schema": "public", - "values": [ - "payment_failed", - "dispute" - ] + "values": ["payment_failed", "dispute"] }, "public.billing_entity_type": { "name": "billing_entity_type", "schema": "public", - "values": [ - "user", - "organization" - ] + "values": ["user", "organization"] }, "public.chat_type": { "name": "chat_type", "schema": "public", - "values": [ - "mothership", - "copilot" - ] + "values": ["mothership", "copilot"] }, "public.copilot_async_tool_status": { "name": "copilot_async_tool_status", "schema": "public", - "values": [ - "pending", - "running", - "completed", - "failed", - "cancelled", - "delivered" - ] + "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" - ] + "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" - ] + "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" - ] + "values": ["invited", "delivery_failed", "in_progress", "completed", "revoked"] }, "public.credential_group_status": { "name": "credential_group_status", "schema": "public", - "values": [ - "active", - "disabled" - ] + "values": ["active", "disabled"] }, "public.credential_member_role": { "name": "credential_member_role", "schema": "public", - "values": [ - "admin", - "member" - ] + "values": ["admin", "member"] }, "public.credential_member_status": { "name": "credential_member_status", "schema": "public", - "values": [ - "active", - "pending", - "revoked" - ] + "values": ["active", "pending", "revoked"] }, "public.credential_type": { "name": "credential_type", @@ -31486,167 +29986,92 @@ "public.data_drain_cadence": { "name": "data_drain_cadence", "schema": "public", - "values": [ - "hourly", - "daily" - ] + "values": ["hourly", "daily"] }, "public.data_drain_destination": { "name": "data_drain_destination", "schema": "public", - "values": [ - "s3", - "gcs", - "azure_blob", - "datadog", - "bigquery", - "snowflake", - "webhook" - ] + "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" - ] + "values": ["running", "success", "failed"] }, "public.data_drain_run_trigger": { "name": "data_drain_run_trigger", "schema": "public", - "values": [ - "cron", - "manual" - ] + "values": ["cron", "manual"] }, "public.data_drain_source": { "name": "data_drain_source", "schema": "public", - "values": [ - "workflow_logs", - "job_logs", - "audit_logs", - "copilot_chats", - "copilot_runs" - ] + "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" - ] + "values": ["execution_log", "paused_snapshot"] }, "public.folder_resource_type": { "name": "folder_resource_type", "schema": "public", - "values": [ - "workflow", - "file", - "knowledge_base", - "table" - ] + "values": ["workflow", "file", "knowledge_base", "table"] }, "public.invitation_kind": { "name": "invitation_kind", "schema": "public", - "values": [ - "organization", - "workspace" - ] + "values": ["organization", "workspace"] }, "public.invitation_membership_intent": { "name": "invitation_membership_intent", "schema": "public", - "values": [ - "internal", - "external" - ] + "values": ["internal", "external"] }, "public.invitation_status": { "name": "invitation_status", "schema": "public", - "values": [ - "pending", - "accepted", - "rejected", - "cancelled", - "expired" - ] + "values": ["pending", "accepted", "rejected", "cancelled", "expired"] }, "public.managed_oauth_credential_status": { "name": "managed_oauth_credential_status", "schema": "public", - "values": [ - "active", - "needs_reauth", - "revoked" - ] + "values": ["active", "needs_reauth", "revoked"] }, "public.permission_type": { "name": "permission_type", "schema": "public", - "values": [ - "admin", - "write", - "read" - ] + "values": ["admin", "write", "read"] }, "public.sandbox_image_status": { "name": "sandbox_image_status", "schema": "public", - "values": [ - "pending", - "building", - "ready", - "failed" - ] + "values": ["pending", "building", "ready", "failed"] }, "public.sandbox_language": { "name": "sandbox_language", "schema": "public", - "values": [ - "javascript", - "python" - ] + "values": ["javascript", "python"] }, "public.secret_usage_scope": { "name": "secret_usage_scope", "schema": "public", - "values": [ - "workspace", - "personal" - ] + "values": ["workspace", "personal"] }, "public.secret_usage_source": { "name": "secret_usage_source", "schema": "public", - "values": [ - "workflow", - "copilot", - "mcp" - ] + "values": ["workflow", "copilot", "mcp"] }, "public.upload_session_method": { "name": "upload_session_method", "schema": "public", - "values": [ - "put", - "multipart" - ] + "values": ["put", "multipart"] }, "public.upload_session_provider": { "name": "upload_session_provider", "schema": "public", - "values": [ - "local", - "s3", - "blob", - "gcs" - ] + "values": ["local", "s3", "blob", "gcs"] }, "public.upload_session_purpose": { "name": "upload_session_purpose", @@ -31679,12 +30104,7 @@ "public.usage_log_category": { "name": "usage_log_category", "schema": "public", - "values": [ - "model", - "fixed", - "tool", - "model_unbilled" - ] + "values": ["model", "fixed", "tool", "model_unbilled"] }, "public.usage_log_source": { "name": "usage_log_source", @@ -31706,34 +30126,17 @@ "public.workspace_file_search_index_status": { "name": "workspace_file_search_index_status", "schema": "public", - "values": [ - "pending", - "ready", - "skipped", - "failed" - ] + "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" - ] + "values": ["upload", "user", "api", "copilot", "workflow", "collab", "revert", "unknown"] }, "public.workspace_fork_promote_direction": { "name": "workspace_fork_promote_direction", "schema": "public", - "values": [ - "push", - "pull" - ] + "values": ["push", "pull"] }, "public.workspace_fork_resource_type": { "name": "workspace_fork_resource_type", @@ -31759,11 +30162,7 @@ "public.workspace_mode": { "name": "workspace_mode", "schema": "public", - "values": [ - "personal", - "organization", - "grandfathered_shared" - ] + "values": ["personal", "organization", "grandfathered_shared"] } }, "schemas": {}, diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 5d46ec75d45..09eed673bb0 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -567,7 +567,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { for (let attempt = 0; attempt < 100; attempt++) { if ( ( - await sql`SELECT 1 FROM pg_stat_activity WHERE query LIKE '%CALL pg_temp.backfill_project_families%' AND wait_event = 'PgSleep'` + await sql`SELECT 1 FROM pg_stat_activity WHERE datname = current_database() AND query LIKE '%CALL pg_temp.backfill_project_families%' AND wait_event = 'PgSleep'` ).length ) { retrying = true @@ -660,7 +660,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sleep(10) } expect(committed).toBeTruthy() - await sql`SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND query LIKE '%CALL pg_temp.backfill_project_families%'` + await sql`SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND pid <> pg_backend_pid() AND query LIKE '%CALL pg_temp.backfill_project_families%'` expect(getPostgresErrorCode(await result)).toBe('57014') } finally { release.resolve() From 5e8380d3364804bc2f34e5698da6ba32d79e931e Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 17:50:28 -0700 Subject: [PATCH 048/113] feat(projects): prepare workspace project column compatibility --- apps/sim/app/api/workspaces/[id]/route.ts | 5 +- .../ee/workspace-forking/lib/create-fork.ts | 1 + .../__integration__/foundation.integration.ts | 315 +- apps/sim/lib/projects/account-deletion.ts | 9 +- apps/sim/lib/projects/membership.ts | 28 +- apps/sim/lib/users/account-deletion.ts | 11 +- .../__integration__/fork-sync.integration.ts | 25 +- apps/sim/lib/workspaces/create.ts | 23 +- apps/sim/lib/workspaces/list.ts | 5 +- .../0403_workspace_project_column.sql | 16 + .../db/migrations/meta/0403_snapshot.json | 30216 ++++++++++++++++ packages/db/migrations/meta/_journal.json | 7 + packages/db/schema.ts | 4 + .../workspace-project-column.integration.ts | 115 + .../src/mocks/schema-tables.generated.ts | 1 + 15 files changed, 30652 insertions(+), 129 deletions(-) create mode 100644 packages/db/migrations/0403_workspace_project_column.sql create mode 100644 packages/db/migrations/meta/0403_snapshot.json create mode 100644 packages/db/scripts/workspace-project-column.integration.ts diff --git a/apps/sim/app/api/workspaces/[id]/route.ts b/apps/sim/app/api/workspaces/[id]/route.ts index 754c75bc2d8..355cf4a492e 100644 --- a/apps/sim/app/api/workspaces/[id]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/route.ts @@ -1,6 +1,7 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { workflow } from '@sim/db/schema' import { createLogger } from '@sim/logger' +import { omit } from '@sim/utils/object' import { and, eq, isNull } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { deleteWorkspaceBodySchema, updateWorkspaceContract } from '@/lib/api/contracts' @@ -51,7 +52,7 @@ export const GET = withRouteHandler( return NextResponse.json({ workspace: { - ...workspaceDetails, + ...omit(workspaceDetails, ['projectId']), permissions: userPermission, }, }) @@ -216,7 +217,7 @@ export const PATCH = withRouteHandler( return NextResponse.json({ workspace: { - ...updatedWorkspace, + ...omit(updatedWorkspace, ['projectId']), permissions: userPermission, }, }) diff --git a/apps/sim/ee/workspace-forking/lib/create-fork.ts b/apps/sim/ee/workspace-forking/lib/create-fork.ts index 341fc16117f..4ac678d3354 100644 --- a/apps/sim/ee/workspace-forking/lib/create-fork.ts +++ b/apps/sim/ee/workspace-forking/lib/create-fork.ts @@ -303,6 +303,7 @@ export async function createFork(params: CreateForkParams): Promise generateId()) - let projectId = '' - await db.transaction(async (tx) => { - for (const [index, id] of ids.entries()) { + const projectId = await db.transaction(async (tx) => { + const id = await createProjectRecord(tx, { + name: 'Environment 0', + organizationId, + ownerId, + }) + for (const [index, workspaceId] of ids.entries()) { await tx.insert(workspace).values({ - id, + id: workspaceId, + projectId: id, name: `Environment ${index}`, ownerId, billedAccountUserId: ownerId, @@ -148,19 +153,13 @@ async function fixture(org = true, count = 2) { await tx.insert(permissions).values({ id: generateId(), entityType: 'workspace', - entityId: id, + entityId: workspaceId, userId: ownerId, permissionType: 'admin', }) - if (!index) - projectId = await createProjectForWorkspace(tx, { - workspaceId: id, - name: 'Environment 0', - organizationId, - ownerId, - }) - else await tx.insert(projectWorkspace).values({ projectId, workspaceId: id }) + await tx.insert(projectWorkspace).values({ projectId: id, workspaceId }) } + return id }) const owner = createSessionPrincipal({ userId: ownerId }) const teammate = createSessionPrincipal({ userId: teammateId }) @@ -198,20 +197,22 @@ async function waitUntilBlockedBy(blockerPid: number, waitingIn: string) { async function addOrganizationProject(organizationId: string, ownerId: string) { const workspaceId = generateId() const projectId = await db.transaction(async (tx) => { + const id = await createProjectRecord(tx, { + name: 'Sibling environment', + organizationId, + ownerId, + }) await tx.insert(workspace).values({ id: workspaceId, + projectId: id, name: 'Sibling environment', ownerId, billedAccountUserId: ownerId, organizationId, workspaceMode: 'organization', }) - return createProjectForWorkspace(tx, { - workspaceId, - name: 'Sibling environment', - organizationId, - ownerId, - }) + await tx.insert(projectWorkspace).values({ projectId: id, workspaceId }) + return id }) return { workspaceId, projectId } } @@ -250,7 +251,7 @@ afterAll(async () => { describe('Project foundation at the database and application boundary', () => { check( - 'workspace creation and fork/disconnect assign Projects while APIs remain disabled', + 'workspace creation and fork/disconnect assign both memberships while APIs remain disabled', async () => { setProjectsEnabled(false) const f = await fixture(false, 1) @@ -266,9 +267,12 @@ describe('Project foundation at the database and application boundary', () => { skipDefaultWorkflow: true, }) ) - expect( - await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, source.id)) - ).toHaveLength(1) + const [sourceMembership] = await db + .select({ projectId: projectWorkspace.projectId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, source.id)) + const [persistedSource] = await db.select().from(workspace).where(eq(workspace.id, source.id)) + expect(persistedSource.projectId).toBe(sourceMembership.projectId) const parent = await getWorkspaceWithOwner(source.id) if (!parent) throw new Error('Missing source fixture') const fork = await createFork({ @@ -277,15 +281,25 @@ describe('Project foundation at the database and application boundary', () => { userId: f.ownerId, name: 'Legacy child', }) - expect( - await db - .select() - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) - ).toHaveLength(1) + const [forkMembership] = await db + .select({ projectId: projectWorkspace.projectId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) + expect(forkMembership.projectId).toBe(sourceMembership.projectId) + const [persistedFork] = await db + .select() + .from(workspace) + .where(eq(workspace.id, fork.workspace.id)) + expect(persistedFork.projectId).toBe(sourceMembership.projectId) await unlinkForkEdge({ parentWorkspaceId: source.id, childWorkspaceId: fork.workspace.id }) const [child] = await db.select().from(workspace).where(eq(workspace.id, fork.workspace.id)) + const [detachedMembership] = await db + .select({ projectId: projectWorkspace.projectId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, child.id)) expect(child.forkedFromWorkspaceId).toBeNull() + expect(child.projectId).toBe(detachedMembership.projectId) + expect(child.projectId).not.toBe(sourceMembership.projectId) expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(3) } ) @@ -344,13 +358,13 @@ describe('Project foundation at the database and application boundary', () => { name: 'Assigned child', }) const [membership] = await db - .select() + .select({ projectId: projectWorkspace.projectId, workspaceId: projectWorkspace.workspaceId }) .from(projectWorkspace) .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) expect(membership.projectId).toBe(f.projectId) await unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: fork.workspace.id }) const [detached] = await db - .select() + .select({ projectId: projectWorkspace.projectId, workspaceId: projectWorkspace.workspaceId }) .from(projectWorkspace) .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) expect(detached.projectId).not.toBe(f.projectId) @@ -392,6 +406,8 @@ describe('Project foundation at the database and application boundary', () => { const f = await fixture(false, 1) await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) await db.delete(project).where(eq(project.id, f.projectId)) + const [legacyWorkspace] = await db.select().from(workspace).where(eq(workspace.id, f.ids[0])) + expect(legacyWorkspace.projectId).toBe(f.projectId) const read = createDeferred() const release = createDeferred() const writer = db.transaction(async (tx) => { @@ -419,61 +435,87 @@ describe('Project foundation at the database and application boundary', () => { await tx.execute( sql`SELECT pg_advisory_xact_lock(hashtextextended(${projectBackfillLockKey(f.ids[0])}, 0))` ) - await createProjectForWorkspace(tx, { - workspaceId: f.ids[0], + const projectId = await createProjectRecord(tx, { name: 'Concurrent legacy edit', organizationId: null, ownerId: f.ownerId, }) + await tx.insert(projectWorkspace).values({ projectId, workspaceId: f.ids[0] }) }) expect( - await db.select().from(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + await db + .select({ + projectId: projectWorkspace.projectId, + workspaceId: projectWorkspace.workspaceId, + }) + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, f.ids[0])) ).toHaveLength(1) } ) - check('a fork waiting for backfill inherits membership committed before it resumes', async () => { - const f = await fixture(false, 1) - await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) - await db.delete(project).where(eq(project.id, f.projectId)) - const parent = await getWorkspaceWithOwner(f.ids[0]) - if (!parent) throw new Error('Missing source fixture') - const policy = await getWorkspaceCreationPolicy({ userId: f.ownerId }) - const locked = createDeferred() - const release = createDeferred() - const backfill = db.transaction(async (tx) => { - await tx.execute( - sql`SELECT pg_advisory_xact_lock(hashtextextended(${projectBackfillLockKey(f.ids[0])}, 0))` - ) - const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) - locked.resolve(connection.pid) - await release.promise - return createProjectForWorkspace(tx, { - workspaceId: f.ids[0], - name: 'Backfilled source', - organizationId: null, - ownerId: f.ownerId, + check( + 'a fork waiting for an old writer inherits its membership despite a stale column', + async () => { + const f = await fixture(false, 1) + await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + await db.delete(project).where(eq(project.id, f.projectId)) + const parent = await getWorkspaceWithOwner(f.ids[0]) + if (!parent) throw new Error('Missing source fixture') + const policy = await getWorkspaceCreationPolicy({ userId: f.ownerId }) + const locked = createDeferred() + const release = createDeferred() + const backfill = db.transaction(async (tx) => { + await tx.execute( + sql`SELECT pg_advisory_xact_lock(hashtextextended(${projectBackfillLockKey(f.ids[0])}, 0))` + ) + const [connection] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + locked.resolve(connection.pid) + await release.promise + const projectId = await createProjectRecord(tx, { + name: 'Backfilled source', + organizationId: null, + ownerId: f.ownerId, + }) + await tx.insert(projectWorkspace).values({ projectId, workspaceId: f.ids[0] }) + return projectId }) - }) - await Promise.race([locked.promise, backfill]) - const fork = createFork({ - source: parent, - policy, - userId: f.ownerId, - name: 'Concurrent child', - }) - try { - await waitUntilBlockedBy(await locked.promise, "lock='project_backfill'") - } finally { - release.resolve() + await Promise.race([locked.promise, backfill]) + const fork = createFork({ + source: parent, + policy, + userId: f.ownerId, + name: 'Concurrent child', + }) + try { + await waitUntilBlockedBy(await locked.promise, "lock='project_backfill'") + } finally { + release.resolve() + } + const [projectId, result] = await Promise.all([backfill, fork]) + const [membership] = await db + .select({ + projectId: projectWorkspace.projectId, + workspaceId: projectWorkspace.workspaceId, + }) + .from(projectWorkspace) + .where(eq(projectWorkspace.workspaceId, result.workspace.id)) + expect(membership.projectId).toBe(projectId) + const [persistedFork] = await db + .select() + .from(workspace) + .where(eq(workspace.id, result.workspace.id)) + expect(persistedFork.projectId).toBe(projectId) + const [legacyParent] = await db.select().from(workspace).where(eq(workspace.id, f.ids[0])) + expect(legacyParent.projectId).toBe(f.projectId) + const resolved = await getWorkspaceProject.execute({ + principal: f.owner, + input: { workspaceId: f.ids[0] }, + request, + }) + expect(resolved.project.id).toBe(projectId) } - const [projectId, result] = await Promise.all([backfill, fork]) - const [membership] = await db - .select() - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, result.workspace.id)) - expect(membership.projectId).toBe(projectId) - }) + ) check( 'Project creation commits its named first environment and starter workflow in the requested scope', @@ -509,6 +551,7 @@ describe('Project foundation at the database and application boundary', () => { .from(workspace) .where(eq(workspace.id, result.initialEnvironment.id)) expect(env).toMatchObject({ + projectId: created.id, name: 'Production', organizationId, ownerId: f.teammateId, @@ -631,6 +674,13 @@ describe('Project foundation at the database and application boundary', () => { expect( await db.select().from(workspace).where(eq(workspace.ownerId, f.ownerId)) ).toHaveLength(1) + expect( + await db + .select({ workspaceId: projectWorkspace.workspaceId }) + .from(projectWorkspace) + .innerJoin(project, eq(project.id, projectWorkspace.projectId)) + .where(eq(project.ownerId, f.ownerId)) + ).toEqual([{ workspaceId: f.ids[0] }]) expect( await db.select().from(permissions).where(eq(permissions.userId, f.ownerId)) ).toEqual(before) @@ -930,7 +980,13 @@ describe('Project foundation at the database and application boundary', () => { workflows.every((row) => row.archivedAt !== null && !row.isDeployed && !row.isPublicApi) ).toBe(true) expect( - await db.select().from(projectWorkspace).where(eq(projectWorkspace.projectId, f.projectId)) + await db + .select({ + projectId: projectWorkspace.projectId, + workspaceId: projectWorkspace.workspaceId, + }) + .from(projectWorkspace) + .where(eq(projectWorkspace.projectId, f.projectId)) ).toHaveLength(2) await expect(getProjectIssueAccess.execute(args)).rejects.toMatchObject({ code: 'conflict' }) } @@ -985,6 +1041,25 @@ describe('Project foundation at the database and application boundary', () => { }) } const edge = { parentWorkspaceId: f.ids[0], childWorkspaceId: f.ids[1] } + await expect( + db.transaction(async (tx) => { + await splitForkProject(tx, f.ids[1]) + throw new Error('Abort compound disconnect') + }) + ).rejects.toThrow('Abort compound disconnect') + expect( + await db + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(inArray(workspace.id, f.ids)) + ).toEqual(f.ids.map(() => ({ projectId: f.projectId }))) + expect( + await db + .select({ projectId: projectWorkspace.projectId }) + .from(projectWorkspace) + .where(inArray(projectWorkspace.workspaceId, f.ids)) + ).toEqual(f.ids.map(() => ({ projectId: f.projectId }))) + expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(1) expect(await unlinkForkEdge(edge)).toEqual({ unlinked: true }) for (const table of [ workspaceForkResourceMap, @@ -999,7 +1074,10 @@ describe('Project foundation at the database and application boundary', () => { expect(rows).toEqual([{ childWorkspaceId: f.ids[2] }]) } const first = await db - .select() + .select({ + projectId: projectWorkspace.projectId, + workspaceId: projectWorkspace.workspaceId, + }) .from(projectWorkspace) .where(inArray(projectWorkspace.workspaceId, f.ids)) const root = first.find((row) => row.workspaceId === f.ids[0]) @@ -1009,13 +1087,27 @@ describe('Project foundation at the database and application boundary', () => { expect(root.projectId).toBe(f.projectId) expect(child.projectId).not.toBe(f.projectId) expect(grandchild.projectId).toBe(child.projectId) + const environments = await db + .select({ id: workspace.id, projectId: workspace.projectId }) + .from(workspace) + .where(inArray(workspace.id, f.ids)) + expect(environments).toEqual( + expect.arrayContaining([ + { id: f.ids[0], projectId: f.projectId }, + { id: f.ids[1], projectId: child.projectId }, + { id: f.ids[2], projectId: child.projectId }, + ]) + ) expect(await unlinkForkEdge(edge)).toEqual({ unlinked: false }) const [group] = await db.select().from(permissionGroup).where(eq(permissionGroup.id, groupId)) expect(group.config).toEqual({ deniedPartialAccessProjectIssues: [f.projectId, child.projectId], }) const second = await db - .select() + .select({ + projectId: projectWorkspace.projectId, + workspaceId: projectWorkspace.workspaceId, + }) .from(projectWorkspace) .where(eq(projectWorkspace.workspaceId, f.ids[1])) expect(second[0].projectId).toBe(child.projectId) @@ -1031,7 +1123,10 @@ describe('Project foundation at the database and application boundary', () => { unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: f.ids[1] }) ).rejects.toMatchObject({ code: 'conflict' }) const [child] = await db - .select() + .select({ + projectId: projectWorkspace.projectId, + workspaceId: projectWorkspace.workspaceId, + }) .from(projectWorkspace) .where(eq(projectWorkspace.workspaceId, f.ids[1])) expect(child.projectId).toBe(f.projectId) @@ -1154,7 +1249,10 @@ describe('Project foundation at the database and application boundary', () => { const plan = await getAccountDeletionPlan(f.ownerId) expect(plan.workspacesToDelete.map((row) => row.id)).toEqual([f.ids[0]]) expect(plan.blockers).toEqual([]) - await db.transaction((tx) => prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[0]])) + await db.transaction(async (tx) => { + expect(await prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[0]])).toEqual([]) + await tx.delete(workspace).where(eq(workspace.id, f.ids[0])) + }) const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) expect(record.archivedAt).not.toBeNull() expect(record.ownerId).toBe(f.teammateId) @@ -1173,7 +1271,9 @@ describe('Project foundation at the database and application boundary', () => { role: 'admin', createdAt: new Date(), }) - await db.transaction((tx) => prepareProjectsForAccountDeletion(tx, f.ownerId, [])) + await db.transaction(async (tx) => { + expect(await prepareProjectsForAccountDeletion(tx, f.ownerId, [])).toEqual([]) + }) const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) expect(record.ownerId).toBe(f.teammateId) } @@ -1198,9 +1298,12 @@ describe('Project foundation at the database and application boundary', () => { .where(eq(workspace.id, f.ids[1])) }) const blocker = await held.promise - const deletion = db.transaction((tx) => - prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[1]]) - ) + const deletion = db.transaction(async (tx) => { + const projectIds = await prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[1]]) + expect(projectIds).toHaveLength(1) + await tx.delete(workspace).where(eq(workspace.id, f.ids[1])) + await tx.delete(project).where(inArray(project.id, projectIds)) + }) try { await waitUntilBlockedBy(blocker, "lock='project'") } finally { @@ -1258,13 +1361,49 @@ describe('Project foundation at the database and application boundary', () => { entityId: f.ids[1], permissionType: 'admin', }) - await db.transaction((tx) => prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[0]])) + await db.transaction(async (tx) => { + expect(await prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[0]])).toEqual([]) + await tx.delete(workspace).where(eq(workspace.id, f.ids[0])) + }) const [survivor] = await db.select().from(project).where(eq(project.id, f.projectId)) expect(survivor.ownerId).toBe(f.teammateId) const privateProject = await fixture(false, 1) - await db.transaction((tx) => - prepareProjectsForAccountDeletion(tx, privateProject.ownerId, privateProject.ids) - ) + await expect( + db.transaction(async (tx) => { + const projectIds = await prepareProjectsForAccountDeletion( + tx, + privateProject.ownerId, + privateProject.ids + ) + expect(projectIds).toEqual([privateProject.projectId]) + expect( + await tx.select().from(project).where(eq(project.id, privateProject.projectId)) + ).toHaveLength(1) + throw new Error('Workspace deletion refused') + }) + ).rejects.toThrow('Workspace deletion refused') + expect( + await db + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(inArray(workspace.id, privateProject.ids)) + ).toEqual([{ projectId: privateProject.projectId }]) + expect( + await db + .select({ workspaceId: projectWorkspace.workspaceId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.projectId, privateProject.projectId)) + ).toEqual([{ workspaceId: privateProject.ids[0] }]) + await db.transaction(async (tx) => { + const projectIds = await prepareProjectsForAccountDeletion( + tx, + privateProject.ownerId, + privateProject.ids + ) + expect(projectIds).toEqual([privateProject.projectId]) + await tx.delete(workspace).where(inArray(workspace.id, privateProject.ids)) + await tx.delete(project).where(inArray(project.id, projectIds)) + }) expect( await db.select().from(project).where(eq(project.id, privateProject.projectId)) ).toEqual([]) diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts index 7b0370d82f5..a150b13869e 100644 --- a/apps/sim/lib/projects/account-deletion.ts +++ b/apps/sim/lib/projects/account-deletion.ts @@ -182,12 +182,12 @@ export async function getProjectAccountDeletionBlockers( return blockers } -/** Account teardown may erase a wholly private Project, but never strand a surviving one. */ +/** Transfers surviving Projects and returns private Projects to delete after their workspaces. */ export async function prepareProjectsForAccountDeletion( tx: DbTransaction, userId: string, doomedWorkspaceIds: string[] -): Promise { +): Promise { const ownedEnvironments = await tx .select({ id: workspace.id }) .from(workspace) @@ -213,6 +213,7 @@ export async function prepareProjectsForAccountDeletion( records.map((record) => record.id) ) const doomed = new Set(doomedWorkspaceIds) + const projectsToDelete: string[] = [] const now = new Date() for (const record of records) { const decision = await planProjectDeletion( @@ -225,8 +226,7 @@ export async function prepareProjectsForAccountDeletion( ) if ('blocker' in decision) throw new ProjectConflictError(decision.blocker) if ('remove' in decision) { - await tx.delete(projectWorkspace).where(eq(projectWorkspace.projectId, record.id)) - await tx.delete(project).where(eq(project.id, record.id)) + projectsToDelete.push(record.id) continue } if (!decision.archive && !decision.ownerId) continue @@ -239,4 +239,5 @@ export async function prepareProjectsForAccountDeletion( }) .where(eq(project.id, record.id)) } + return projectsToDelete } diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index 536f95133a6..2543c23b5f7 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -117,10 +117,10 @@ function forkSubtree(workspaceId: string): SQL { )` } -export async function createProjectForWorkspace( +/** Creates the Project before its first workspace; the caller links both in the same transaction. */ +export async function createProjectRecord( tx: DbTransaction, input: { - workspaceId: string name: string organizationId: string | null ownerId: string @@ -134,7 +134,6 @@ export async function createProjectForWorkspace( organizationId: input.organizationId, ownerId: input.ownerId, }) - await tx.insert(projectWorkspace).values({ projectId: id, workspaceId: input.workspaceId }) return id } @@ -143,7 +142,7 @@ export async function lockWorkspaceProject(tx: DbTransaction, workspaceId: strin return withProjectLockTimeout(tx, PROJECT_CHANGING, async () => { await acquireBackfillWriteLocks(tx, [workspaceId]) const [membership] = await tx - .select() + .select({ projectId: projectWorkspace.projectId }) .from(projectWorkspace) .where(eq(projectWorkspace.workspaceId, workspaceId)) .limit(1) @@ -277,12 +276,21 @@ export async function splitForkProject( organizationId: owner.organizationId, ownerId: root.owner_id, }) - await tx - .update(projectWorkspace) - .set({ projectId: id }) - .where( - and(eq(projectWorkspace.projectId, owner.id), inArray(projectWorkspace.workspaceId, ids)) - ) + await withProjectLockTimeout(tx, PROJECT_CHANGING, async () => { + await tx + .select({ id: workspace.id }) + .from(workspace) + .where(inArray(workspace.id, ids)) + .orderBy(asc(workspace.id)) + .for('no key update') + await tx.update(workspace).set({ projectId: id }).where(inArray(workspace.id, ids)) + await tx + .update(projectWorkspace) + .set({ projectId: id }) + .where( + and(eq(projectWorkspace.projectId, owner.id), inArray(projectWorkspace.workspaceId, ids)) + ) + }) if (owner.organizationId) { await acquirePermissionGroupOrgLock(tx, owner.organizationId) await tx.execute(sql` diff --git a/apps/sim/lib/users/account-deletion.ts b/apps/sim/lib/users/account-deletion.ts index f748152778f..5fe50700838 100644 --- a/apps/sim/lib/users/account-deletion.ts +++ b/apps/sim/lib/users/account-deletion.ts @@ -6,6 +6,7 @@ import { member, organization, permissions, + project, tableRunDispatches, uploadSession, user, @@ -694,7 +695,11 @@ export async function deleteUserAccount(userId: string): Promise { - await prepareProjectsForAccountDeletion(tx, userId, doomedWorkspaceIds) + const projectIdsToDelete = await prepareProjectsForAccountDeletion( + tx, + userId, + doomedWorkspaceIds + ) if (doomedWorkspaceIds.length > 0) { /** * Re-checked here rather than trusted from the plan: a workspace that @@ -735,6 +740,10 @@ export async function deleteUserAccount(userId: string): Promise { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ - id: sourceWorkspaceId, - name: 'Fork source fixture', - ownerId: userId, - billedAccountUserId: userId, - allowPersonalApiKeys: true, - }) - await db.transaction((tx) => - createProjectForWorkspace(tx, { - workspaceId: sourceWorkspaceId, + await db.transaction(async (tx) => { + const projectId = await createProjectRecord(tx, { name: 'Fork source fixture', ownerId: userId, organizationId: null, }) - ) + await tx.insert(workspace).values({ + id: sourceWorkspaceId, + projectId, + name: 'Fork source fixture', + ownerId: userId, + billedAccountUserId: userId, + allowPersonalApiKeys: true, + }) + await tx.insert(projectWorkspace).values({ projectId, workspaceId: sourceWorkspaceId }) + }) await db.insert(permissions).values({ id: generateId(), userId, diff --git a/apps/sim/lib/workspaces/create.ts b/apps/sim/lib/workspaces/create.ts index b764d7982bd..f88aacb9786 100644 --- a/apps/sim/lib/workspaces/create.ts +++ b/apps/sim/lib/workspaces/create.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' -import { permissions, type WorkspaceMode, workspace } from '@sim/db/schema' +import { permissions, projectWorkspace, type WorkspaceMode, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { getPostgresConstraintName, getPostgresErrorCode } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { PlatformEvents } from '@/lib/core/telemetry' import type { DbTransaction } from '@/lib/db/types' -import { createProjectForWorkspace } from '@/lib/projects/membership' +import { createProjectRecord } from '@/lib/projects/membership' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { insertNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' @@ -18,8 +18,9 @@ import { WorkspaceOwnerMissingError, } from '@/lib/workspaces/policy' -/** Foreign keys from `workspace` to `user`; a violation means the acting user's row is gone. */ +/** Creation's foreign keys to `user`; a violation means the acting user's row is gone. */ export const WORKSPACE_USER_FK_CONSTRAINTS = new Set([ + 'project_owner_id_user_id_fk', 'workspace_owner_id_user_id_fk', 'workspace_billed_account_user_id_user_id_fk', ]) @@ -121,8 +122,16 @@ export async function createWorkspaceWithProjectInTransaction( ? lockedCreationContext.billedAccountUserId : billedAccountUserId + const projectId = await createProjectRecord(tx, { + projectName, + name, + organizationId: organizationId ?? null, + ownerId: userId, + }) + await tx.insert(workspace).values({ id: workspaceId, + projectId, name, ownerId: userId, organizationId, @@ -133,13 +142,7 @@ export async function createWorkspaceWithProjectInTransaction( updatedAt: now, }) - const projectId = await createProjectForWorkspace(tx, { - projectName, - workspaceId, - name, - organizationId: organizationId ?? null, - ownerId: userId, - }) + await tx.insert(projectWorkspace).values({ projectId, workspaceId }) const permissionRows = [ { diff --git a/apps/sim/lib/workspaces/list.ts b/apps/sim/lib/workspaces/list.ts index af95085a657..c7c91c044d7 100644 --- a/apps/sim/lib/workspaces/list.ts +++ b/apps/sim/lib/workspaces/list.ts @@ -1,6 +1,7 @@ import { db } from '@sim/db' import { pinnedItem, settings, type workspace as workspaceTable } from '@sim/db/schema' import type { PermissionType } from '@sim/platform-authz/workspace' +import { omit } from '@sim/utils/object' import { and, eq } from 'drizzle-orm' import type { PlanCategory } from '@/lib/billing/plan-helpers' import { @@ -19,7 +20,7 @@ import { listRecentWorkspaceIds, sortByVisitRecency } from '@/lib/workspaces/vis type WorkspaceRow = typeof workspaceTable.$inferSelect /** Accessible workspace row decorated with the viewer's role and invite policy flags. */ -export type WorkspaceWithInviteFlags = WorkspaceRow & +export type WorkspaceWithInviteFlags = Omit & WorkspaceInviteFlags & { role: 'owner' | 'admin' | 'member' permissions: PermissionType @@ -94,7 +95,7 @@ async function buildWorkspacesWithInviteFlags( const invitePolicy = evaluateWorkspaceInvitePolicy(workspaceDetails, { billedPlanCategory }) return { - ...workspaceDetails, + ...omit(workspaceDetails, ['projectId']), role: workspaceDetails.ownerId === userId ? ('owner' as const) diff --git a/packages/db/migrations/0403_workspace_project_column.sql b/packages/db/migrations/0403_workspace_project_column.sql new file mode 100644 index 00000000000..c93ad61f79b --- /dev/null +++ b/packages/db/migrations/0403_workspace_project_column.sql @@ -0,0 +1,16 @@ +ALTER TABLE "workspace" ADD COLUMN IF NOT EXISTS "project_id" text; +--> statement-breakpoint +COMMIT; +--> statement-breakpoint +SET statement_timeout = '15min'; +--> statement-breakpoint +SET lock_timeout = 0; +--> statement-breakpoint +-- Rebuild on replay so an interrupted concurrent build cannot leave an INVALID index behind. +DROP INDEX CONCURRENTLY IF EXISTS "workspace_project_id_id_idx"; +--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_project_id_id_idx" ON "workspace" USING btree ("project_id","id"); +--> statement-breakpoint +SET lock_timeout = '5s'; +--> statement-breakpoint +SET statement_timeout = 0; diff --git a/packages/db/migrations/meta/0403_snapshot.json b/packages/db/migrations/meta/0403_snapshot.json new file mode 100644 index 00000000000..18fca789c1a --- /dev/null +++ b/packages/db/migrations/meta/0403_snapshot.json @@ -0,0 +1,30216 @@ +{ + "id": "1d401009-bd79-4a59-b6d3-723bc22a254c", + "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.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 + }, + "project_id": { + "name": "project_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_project_id_id_idx": { + "name": "workspace_project_id_id_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "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" + ] + }, + "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..9c7f5184329 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": 1791506453532, + "tag": "0403_workspace_project_column", + "breakpoints": true } ] } diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 611943014f2..6b196455f1f 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2018,6 +2018,8 @@ export const workspace = pgTable( organizationId: text('organization_id').references(() => organization.id, { onDelete: 'set null', }), + /** Nullable and unconstrained until older connector-only writers retire. */ + projectId: text('project_id'), workspaceMode: workspaceModeEnum('workspace_mode').notNull().default('grandfathered_shared'), billedAccountUserId: text('billed_account_user_id') .notNull() @@ -2059,6 +2061,7 @@ export const workspace = pgTable( (table) => ({ ownerIdIdx: index('workspace_owner_id_idx').on(table.ownerId), organizationIdIdx: index('workspace_organization_id_idx').on(table.organizationId), + projectIdIdx: index('workspace_project_id_id_idx').on(table.projectId, table.id), nonNegativeStorage: check( 'workspace_storage_used_bytes_non_negative', sql`${table.storageUsedBytes} >= 0` @@ -2114,6 +2117,7 @@ export const project = pgTable( export const projectWorkspace = pgTable( 'project_workspace', { + // contract-pending(after workspace.projectId backfill and required-column enforcement): retire connector readers/writers, then remove this table in a later deployment once older instances drain. projectId: text('project_id') .notNull() .references(() => project.id, { onDelete: 'restrict' }), diff --git a/packages/db/scripts/workspace-project-column.integration.ts b/packages/db/scripts/workspace-project-column.integration.ts new file mode 100644 index 00000000000..c55a2ba307f --- /dev/null +++ b/packages/db/scripts/workspace-project-column.integration.ts @@ -0,0 +1,115 @@ +import { readFileSync } from 'node:fs' +import { applyMigration, withMigrationSchema } from '@sim/db/scripts/migration-fixture' +import { getPostgresErrorCode } from '@sim/utils/errors' +import type postgres from 'postgres' +import { describe, expect, it } from 'vitest' + +const migration = readFileSync( + new URL('../migrations/0403_workspace_project_column.sql', import.meta.url), + 'utf8' +) + +async function fixture(run: (sql: postgres.Sql) => Promise): Promise { + await withMigrationSchema('workspace_project_column', async (sql) => { + await sql`CREATE TABLE project (id text PRIMARY KEY)` + await sql`CREATE TABLE workspace ( + id text PRIMARY KEY, + forked_from_workspace_id text REFERENCES workspace(id) ON DELETE SET NULL + )` + await sql`CREATE TABLE project_workspace ( + project_id text NOT NULL REFERENCES project(id) ON DELETE RESTRICT, + workspace_id text NOT NULL UNIQUE REFERENCES workspace(id) ON DELETE CASCADE, + PRIMARY KEY (project_id, workspace_id) + )` + await sql`INSERT INTO project VALUES ('family'), ('private')` + await sql`INSERT INTO workspace VALUES ('production', NULL), ('staging', 'production'), ('personal', NULL)` + await sql`INSERT INTO project_workspace VALUES + ('family', 'production'), ('family', 'staging'), ('private', 'personal')` + await run(sql) + }) +} + +describe('workspace Project column compatibility migration', () => { + it('preserves existing memberships and permits deployed writers that omit the column', async () => { + await fixture(async (sql) => { + await applyMigration(sql, migration) + + await sql.begin(async (tx) => { + await tx`INSERT INTO workspace (id) VALUES ('old-writer')` + await tx`INSERT INTO project VALUES ('new-project')` + await tx`INSERT INTO project_workspace VALUES ('new-project', 'old-writer')` + }) + + expect( + await sql`SELECT w.id, w.project_id, pw.project_id AS membership, w.forked_from_workspace_id AS parent + FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` + ).toEqual([ + { id: 'old-writer', project_id: null, membership: 'new-project', parent: null }, + { id: 'personal', project_id: null, membership: 'private', parent: null }, + { id: 'production', project_id: null, membership: 'family', parent: null }, + { id: 'staging', project_id: null, membership: 'family', parent: 'production' }, + ]) + }) + }) + + it('allows old account teardown to delete a Project before its populated workspace', async () => { + await fixture(async (sql) => { + await applyMigration(sql, migration) + await sql`UPDATE workspace w SET project_id = pw.project_id + FROM project_workspace pw WHERE pw.workspace_id = w.id` + + await sql.begin(async (tx) => { + await tx`DELETE FROM project_workspace WHERE project_id = 'private'` + await tx`DELETE FROM project WHERE id = 'private'` + await tx`DELETE FROM workspace WHERE id = 'personal'` + }) + + expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'production', project_id: 'family' }, + { id: 'staging', project_id: 'family' }, + ]) + expect(await sql`SELECT id FROM project`).toEqual([{ id: 'family' }]) + expect(await sql`SELECT workspace_id FROM project_workspace ORDER BY workspace_id`).toEqual([ + { workspace_id: 'production' }, + { workspace_id: 'staging' }, + ]) + }) + }) + + it('repairs an interrupted concurrent index build on replay without changing Project assignments', async () => { + await fixture(async (sql) => { + await applyMigration(sql, migration) + await sql`UPDATE workspace w SET project_id = pw.project_id + FROM project_workspace pw WHERE pw.workspace_id = w.id` + await sql`DROP INDEX CONCURRENTLY workspace_project_id_id_idx` + await expect( + sql`CREATE UNIQUE INDEX CONCURRENTLY workspace_project_id_id_idx ON workspace(project_id)` + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23505') + expect( + await sql`SELECT indisvalid FROM pg_index + WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` + ).toEqual([{ indisvalid: false }]) + + await applyMigration(sql, migration) + await applyMigration(sql, migration) + + expect( + await sql`SELECT indisvalid, indisunique FROM pg_index + WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` + ).toEqual([{ indisvalid: true, indisunique: false }]) + expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'personal', project_id: 'private' }, + { id: 'production', project_id: 'family' }, + { id: 'staging', project_id: 'family' }, + ]) + await sql.begin(async (tx) => { + await tx`INSERT INTO workspace (id, project_id, forked_from_workspace_id) + VALUES ('new-fork', 'family', 'production')` + await tx`INSERT INTO project_workspace VALUES ('family', 'new-fork')` + }) + expect( + await sql`SELECT count(*)::int AS count FROM workspace WHERE project_id = 'family'` + ).toEqual([{ count: 3 }]) + }) + }) +}) diff --git a/packages/testing/src/mocks/schema-tables.generated.ts b/packages/testing/src/mocks/schema-tables.generated.ts index 1c89248bda5..815d84d0427 100644 --- a/packages/testing/src/mocks/schema-tables.generated.ts +++ b/packages/testing/src/mocks/schema-tables.generated.ts @@ -486,6 +486,7 @@ export const GENERATED_SCHEMA_TABLES = { 'logoUrl', 'ownerId', 'organizationId', + 'projectId', 'workspaceMode', 'billedAccountUserId', 'storageUsedBytes', From 235d1e2216c8e4682430a931e1cec1f6fea02ac2 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 18:11:19 -0700 Subject: [PATCH 049/113] test(projects): clean up compatibility fixtures atomically --- .../__integration__/foundation.integration.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 2ef38a5ddf9..157872d8767 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -239,14 +239,16 @@ afterAll(async () => { process.env.PROJECT_FOUNDATION_REPORT_PATH ?? resolve('test-results/project-foundation.json') await mkdir(dirname(reportPath), { recursive: true }) await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) - if (users.length) - await db - .delete(workspace) - .where(or(inArray(workspace.ownerId, users), inArray(workspace.billedAccountUserId, users))) - if (users.length) await db.delete(project).where(inArray(project.ownerId, users)) - if (organizations.length) - await db.delete(organization).where(inArray(organization.id, organizations)) - if (users.length) await db.delete(user).where(inArray(user.id, users)) + await db.transaction(async (tx) => { + if (users.length) + await tx + .delete(workspace) + .where(or(inArray(workspace.ownerId, users), inArray(workspace.billedAccountUserId, users))) + if (users.length) await tx.delete(project).where(inArray(project.ownerId, users)) + if (organizations.length) + await tx.delete(organization).where(inArray(organization.id, organizations)) + if (users.length) await tx.delete(user).where(inArray(user.id, users)) + }) }) describe('Project foundation at the database and application boundary', () => { From 202c28ec5c2772e5361ba4fd2091b1997654c270 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 19:51:27 -0700 Subject: [PATCH 050/113] feat(projects): move membership to workspace column with rollout bridge --- .../workspace-forking/lib/create-fork.test.ts | 14 +- .../ee/workspace-forking/lib/create-fork.ts | 7 +- .../__integration__/foundation.integration.ts | 206 ++++++++++- apps/sim/lib/projects/account-deletion.ts | 24 +- .../lib/projects/application/authorization.ts | 17 +- .../sim/lib/projects/application/use-cases.ts | 17 +- apps/sim/lib/projects/lifecycle.ts | 9 +- apps/sim/lib/projects/membership.ts | 66 ++-- .../__integration__/fork-sync.integration.ts | 16 +- apps/sim/lib/workspaces/create.ts | 4 +- .../organization-workspaces.integration.ts | 20 +- .../0403_workspace_project_column.sql | 114 +++++- packages/db/schema.ts | 5 +- .../workspace-project-column.integration.ts | 325 +++++++++++++++++- 14 files changed, 707 insertions(+), 137 deletions(-) diff --git a/apps/sim/ee/workspace-forking/lib/create-fork.test.ts b/apps/sim/ee/workspace-forking/lib/create-fork.test.ts index f8b33a85939..7baa904052a 100644 --- a/apps/sim/ee/workspace-forking/lib/create-fork.test.ts +++ b/apps/sim/ee/workspace-forking/lib/create-fork.test.ts @@ -1,4 +1,4 @@ -import { projectWorkspace, workspace } from '@sim/db/schema' +import { workspace } from '@sim/db/schema' import { dbChainMockFns, queueTableRows, resetDbChainMock } from '@sim/testing' import { workflowsPersistenceUtilsMock } from '@sim/testing/mocks/workflows-persistence-utils.mock' import { @@ -140,8 +140,8 @@ function forkParams(selection?: { describe('createFork storage headroom gate', () => { beforeEach(() => { resetDbChainMock() - queueTableRows(projectWorkspace, [{ projectId: 'project-source' }]) - queueTableRows(projectWorkspace, [ + queueTableRows(workspace, [{ projectId: 'project-source' }]) + queueTableRows(workspace, [ { project: { id: 'project-source', organizationId: null, archivedAt: null } }, ]) /** @@ -215,8 +215,8 @@ describe('createFork storage headroom gate', () => { it('refuses when the parent changed organizations after the policy was captured', async () => { resetDbChainMock() - queueTableRows(projectWorkspace, [{ projectId: 'project-source' }]) - queueTableRows(projectWorkspace, [ + queueTableRows(workspace, [{ projectId: 'project-source' }]) + queueTableRows(workspace, [ { project: { id: 'project-source', organizationId: null, archivedAt: null } }, ]) /** @@ -243,8 +243,8 @@ describe('createFork storage headroom gate', () => { */ it('gives the child the source workspace personal API-key and fork-sync policies', async () => { resetDbChainMock() - queueTableRows(projectWorkspace, [{ projectId: 'project-source' }]) - queueTableRows(projectWorkspace, [ + queueTableRows(workspace, [{ projectId: 'project-source' }]) + queueTableRows(workspace, [ { project: { id: 'project-source', organizationId: null, archivedAt: null } }, ]) queueTableRows(workspace, [{ organizationId: null, forkSyncNewWorkflowsExcluded: true }]) diff --git a/apps/sim/ee/workspace-forking/lib/create-fork.ts b/apps/sim/ee/workspace-forking/lib/create-fork.ts index 4ac678d3354..13f386a6a54 100644 --- a/apps/sim/ee/workspace-forking/lib/create-fork.ts +++ b/apps/sim/ee/workspace-forking/lib/create-fork.ts @@ -1,5 +1,5 @@ import { db } from '@sim/db' -import { permissions, projectWorkspace, workspace } from '@sim/db/schema' +import { permissions, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' import type { PermissionType } from '@sim/platform-authz/workspace' import { getErrorMessage } from '@sim/utils/errors' @@ -317,11 +317,6 @@ export async function createFork(params: CreateForkParams): Promise { +const isolated = await vi.hoisted(async () => { process.env.ADMIN_API_KEY = 'project-fixture-admin-key' + const { execFileSync } = await import('node:child_process') + const { fileURLToPath } = await import('node:url') + const { readTestDatabaseUrl } = await import('@sim/db/testing/test-infrastructure') + const { generateId } = await import('@sim/utils/id') + const { default: postgres } = await import('postgres') + const admin = postgres(readTestDatabaseUrl(), { max: 1 }) + const name = `project_foundation_test_${generateId().replaceAll('-', '')}` + const url = new URL(readTestDatabaseUrl()) + url.pathname = `/${name}` + const databaseUrl = url.toString() + await admin.unsafe(`CREATE DATABASE "${name}"`) + const client = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) + try { + await client.unsafe( + 'CREATE EXTENSION vector; CREATE EXTENSION btree_gin; CREATE EXTENSION pg_trgm' + ) + execFileSync('bun', ['--no-env-file', 'scripts/migrate.ts'], { + cwd: fileURLToPath(new URL('../../../../../packages/db/', import.meta.url)), + env: { ...process.env, DATABASE_URL: databaseUrl, MIGRATION_DATABASE_URL: databaseUrl }, + stdio: 'pipe', + timeout: 120_000, + }) + process.env.DATABASE_URL = databaseUrl + return { admin, name, databaseUrl, client } + } catch (error) { + await client.end() + await admin.unsafe(`DROP DATABASE "${name}" WITH (FORCE)`) + await admin.end() + throw error + } }) vi.mock('@/lib/core/config/feature-flags', () => featureFlagsMock) @@ -157,7 +187,6 @@ async function fixture(org = true, count = 2) { userId: ownerId, permissionType: 'admin', }) - await tx.insert(projectWorkspace).values({ projectId: id, workspaceId }) } return id }) @@ -211,7 +240,6 @@ async function addOrganizationProject(organizationId: string, ownerId: string) { organizationId, workspaceMode: 'organization', }) - await tx.insert(projectWorkspace).values({ projectId: id, workspaceId }) return id }) return { workspaceId, projectId } @@ -234,11 +262,7 @@ async function addWorkflow(workspaceId: string, userId: string) { return id } -afterAll(async () => { - const reportPath = - process.env.PROJECT_FOUNDATION_REPORT_PATH ?? resolve('test-results/project-foundation.json') - await mkdir(dirname(reportPath), { recursive: true }) - await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) +async function clearFixtures() { await db.transaction(async (tx) => { if (users.length) await tx @@ -249,6 +273,23 @@ afterAll(async () => { await tx.delete(organization).where(inArray(organization.id, organizations)) if (users.length) await tx.delete(user).where(inArray(user.id, users)) }) +} + +afterAll(async () => { + try { + const reportPath = + process.env.PROJECT_FOUNDATION_REPORT_PATH ?? resolve('test-results/project-foundation.json') + await mkdir(dirname(reportPath), { recursive: true }) + await writeFile(reportPath, JSON.stringify({ checks }, null, 2)) + } finally { + await db.$client.end({ timeout: 2 }) + await isolated.client.end() + try { + await isolated.admin.unsafe(`DROP DATABASE "${isolated.name}" WITH (FORCE)`) + } finally { + await isolated.admin.end() + } + } }) describe('Project foundation at the database and application boundary', () => { @@ -380,6 +421,34 @@ describe('Project foundation at the database and application boundary', () => { expect(detachedProject.archivedAt).not.toBeNull() }) + check('legacy unassigned forks and disconnects preserve the unassigned lineage', async () => { + const f = await fixture(false, 1) + await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + await db.delete(project).where(eq(project.id, f.projectId)) + const parent = await getWorkspaceWithOwner(f.ids[0]) + if (!parent) throw new Error('Missing legacy source fixture') + const fork = await createFork({ + source: parent, + policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), + userId: f.ownerId, + name: 'Legacy unassigned child', + }) + await expect( + getWorkspaceProject.execute({ + principal: f.owner, + input: { workspaceId: fork.workspace.id }, + request, + }) + ).rejects.toMatchObject({ code: 'not_found' }) + await unlinkForkEdge({ parentWorkspaceId: parent.id, childWorkspaceId: fork.workspace.id }) + const [child] = await db + .select({ projectId: workspace.projectId, parentId: workspace.forkedFromWorkspaceId }) + .from(workspace) + .where(eq(workspace.id, fork.workspace.id)) + expect(child).toEqual({ projectId: null, parentId: null }) + expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toEqual([]) + }) + check('fork refuses a partially assigned lineage', async () => { const f = await fixture(false, 3) await db @@ -409,7 +478,7 @@ describe('Project foundation at the database and application boundary', () => { await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) await db.delete(project).where(eq(project.id, f.projectId)) const [legacyWorkspace] = await db.select().from(workspace).where(eq(workspace.id, f.ids[0])) - expect(legacyWorkspace.projectId).toBe(f.projectId) + expect(legacyWorkspace.projectId).toBeNull() const read = createDeferred() const release = createDeferred() const writer = db.transaction(async (tx) => { @@ -457,7 +526,7 @@ describe('Project foundation at the database and application boundary', () => { ) check( - 'a fork waiting for an old writer inherits its membership despite a stale column', + 'a fork waiting for an old connector writer inherits its synchronized membership', async () => { const f = await fixture(false, 1) await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) @@ -509,7 +578,7 @@ describe('Project foundation at the database and application boundary', () => { .where(eq(workspace.id, result.workspace.id)) expect(persistedFork.projectId).toBe(projectId) const [legacyParent] = await db.select().from(workspace).where(eq(workspace.id, f.ids[0])) - expect(legacyParent.projectId).toBe(f.projectId) + expect(legacyParent.projectId).toBe(projectId) const resolved = await getWorkspaceProject.execute({ principal: f.owner, input: { workspaceId: f.ids[0] }, @@ -1411,4 +1480,119 @@ describe('Project foundation at the database and application boundary', () => { ).toEqual([]) } ) + + check( + 'column-only application operations remain valid after the connector is removed', + async () => { + await clearFixtures() + await isolated.client.unsafe(` + DROP TRIGGER workspace_sync_project_membership ON workspace; + DROP TRIGGER project_workspace_sync_column ON project_workspace; + DROP FUNCTION workspace_sync_project_membership_fn(); + DROP FUNCTION project_workspace_sync_column_fn(); + DROP TABLE project_workspace; + ALTER TABLE workspace ALTER COLUMN project_id SET NOT NULL; + ALTER TABLE workspace ADD CONSTRAINT workspace_project_id_project_id_fk + FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE RESTRICT; + `) + const f = await fixture(true, 1) + const organizationId = f.organizationId + if (!organizationId) throw new Error('Missing organization fixture') + const created = await createProject.execute({ + principal: f.owner, + input: { + organizationId, + name: 'Column-only Project', + initialEnvironment: { name: 'Production' }, + }, + request, + }) + const source = await getWorkspaceWithOwner(created.initialEnvironment.id) + if (!source) throw new Error('Missing source environment') + const resolved = await getWorkspaceProject.execute({ + principal: f.owner, + input: { workspaceId: source.id }, + request, + }) + expect(resolved.project.id).toBe(created.project.id) + await expect( + getProject.execute({ + principal: createSessionPrincipal({ userId: f.outsiderId }), + input: { projectId: created.project.id }, + request, + }) + ).rejects.toMatchObject({ code: 'not_found' }) + const listed = await listProjects.execute({ + principal: f.owner, + input: { limit: 10 }, + request, + }) + expect(listed.projects.map((row) => row.id)).toEqual( + expect.arrayContaining([f.projectId, created.project.id]) + ) + await renameProject.execute({ + principal: f.owner, + input: { projectId: created.project.id, name: 'Renamed without connector' }, + request, + }) + const fork = await createFork({ + source, + policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), + userId: f.ownerId, + name: 'Staging', + }) + const [forkEnvironment] = await db + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(eq(workspace.id, fork.workspace.id)) + expect(forkEnvironment.projectId).toBe(created.project.id) + await unlinkForkEdge({ parentWorkspaceId: source.id, childWorkspaceId: fork.workspace.id }) + const [detached] = await db + .select({ projectId: workspace.projectId, parentId: workspace.forkedFromWorkspaceId }) + .from(workspace) + .where(eq(workspace.id, fork.workspace.id)) + if (!detached.projectId) throw new Error('Disconnected environment has no Project') + expect(detached.projectId).not.toBe(created.project.id) + expect(detached.parentId).toBeNull() + await archiveWorkspace(fork.workspace.id, request) + const [detachedProject] = await db + .select({ archivedAt: project.archivedAt }) + .from(project) + .where(eq(project.id, detached.projectId)) + expect(detachedProject.archivedAt).not.toBeNull() + await db.transaction(async (tx) => { + await detachOrganizationWorkspacesTx(tx, organizationId) + await tx.delete(organization).where(eq(organization.id, organizationId)) + }) + await archiveProject.execute({ + principal: f.owner, + input: { projectId: created.project.id }, + request, + }) + const [archived] = await db.select().from(project).where(eq(project.id, created.project.id)) + expect(archived.name).toBe('Renamed without connector') + expect(archived.organizationId).toBeNull() + expect(archived.archivedAt).not.toBeNull() + expect( + await db + .select({ id: workflow.id }) + .from(workflow) + .where(and(eq(workflow.workspaceId, source.id), sql`${workflow.archivedAt} IS NULL`)) + ).toEqual([]) + const privateProject = await fixture(false, 1) + await db.transaction(async (tx) => { + const projectIds = await prepareProjectsForAccountDeletion( + tx, + privateProject.ownerId, + privateProject.ids + ) + expect(projectIds).toEqual([privateProject.projectId]) + await tx.delete(workspace).where(inArray(workspace.id, privateProject.ids)) + await tx.delete(project).where(inArray(project.id, projectIds)) + }) + expect( + await db.select().from(project).where(eq(project.id, privateProject.projectId)) + ).toEqual([]) + } + ) }) diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts index a150b13869e..fa1c4a32df0 100644 --- a/apps/sim/lib/projects/account-deletion.ts +++ b/apps/sim/lib/projects/account-deletion.ts @@ -1,5 +1,5 @@ import { db } from '@sim/db' -import { member, permissions, project, projectWorkspace, workspace } from '@sim/db/schema' +import { member, permissions, project, workspace } from '@sim/db/schema' import { ORG_ADMIN_ROLES } from '@sim/platform-authz/workspace' import { and, asc, eq, inArray, ne, or, sql } from 'drizzle-orm' import type { DbOrTx, DbTransaction } from '@/lib/db/types' @@ -13,22 +13,18 @@ import { async function loadRelatedProjects(executor: DbOrTx, userId: string, doomedWorkspaceIds: string[]) { const doomedMemberships = doomedWorkspaceIds.length ? await executor - .select({ projectId: projectWorkspace.projectId }) - .from(projectWorkspace) - .where(inArray(projectWorkspace.workspaceId, doomedWorkspaceIds)) + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(inArray(workspace.id, doomedWorkspaceIds)) : [] + const projectIds = doomedMemberships.flatMap((row) => (row.projectId ? [row.projectId] : [])) return executor .select() .from(project) .where( or( eq(project.ownerId, userId), - doomedMemberships.length - ? inArray( - project.id, - doomedMemberships.map((row) => row.projectId) - ) - : undefined + projectIds.length ? inArray(project.id, projectIds) : undefined ) ) .orderBy(asc(project.id)) @@ -108,16 +104,16 @@ async function loadProjectEnvironments(executor: DbOrTx, projectIds: string[]) { const rows = projectIds.length ? await executor .select({ - projectId: projectWorkspace.projectId, + projectId: workspace.projectId, id: workspace.id, archivedAt: workspace.archivedAt, }) - .from(projectWorkspace) - .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) - .where(inArray(projectWorkspace.projectId, projectIds)) + .from(workspace) + .where(inArray(workspace.projectId, projectIds)) : [] const byProject = new Map() for (const { projectId, ...environment } of rows) { + if (!projectId) continue const environments = byProject.get(projectId) if (environments) environments.push(environment) else byProject.set(projectId, [environment]) diff --git a/apps/sim/lib/projects/application/authorization.ts b/apps/sim/lib/projects/application/authorization.ts index eab358ab472..cc8c193b35c 100644 --- a/apps/sim/lib/projects/application/authorization.ts +++ b/apps/sim/lib/projects/application/authorization.ts @@ -1,12 +1,5 @@ import type { Principal, SessionPrincipal } from '@sim/auth/principal' -import { - member, - permissionGroup, - permissions, - project, - projectWorkspace, - workspace, -} from '@sim/db/schema' +import { member, permissionGroup, permissions, project, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { isOrgAdminRole } from '@sim/platform-authz/workspace' import { and, asc, eq, inArray, sql } from 'drizzle-orm' @@ -76,18 +69,17 @@ async function loadProjectAccess( : [] const environments = await tx .select({ - projectId: projectWorkspace.projectId, + projectId: workspace.projectId, id: workspace.id, name: workspace.name, organizationId: workspace.organizationId, archivedAt: workspace.archivedAt, parentId: workspace.forkedFromWorkspaceId, }) - .from(projectWorkspace) - .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) + .from(workspace) .where( inArray( - projectWorkspace.projectId, + workspace.projectId, records.map((record) => record.id) ) ) @@ -110,6 +102,7 @@ async function loadProjectAccess( const grantsById = new Map(grants.map((row) => [row.id, row.permission])) const environmentsByProject = new Map() for (const { projectId, ...row } of environments) { + if (!projectId) continue const access = { ...row, permission: grantsById.get(row.id) ?? null } const rows = environmentsByProject.get(projectId) if (rows) rows.push(access) diff --git a/apps/sim/lib/projects/application/use-cases.ts b/apps/sim/lib/projects/application/use-cases.ts index 98cfb3495fc..ece290b4d1d 100644 --- a/apps/sim/lib/projects/application/use-cases.ts +++ b/apps/sim/lib/projects/application/use-cases.ts @@ -1,6 +1,6 @@ import { AuditAction, AuditResourceType } from '@sim/audit' import { db } from '@sim/db' -import { member, permissions, project, projectWorkspace, workspace } from '@sim/db/schema' +import { member, permissions, project, workspace } from '@sim/db/schema' import { ORG_ADMIN_ROLES } from '@sim/platform-authz/workspace' import { eq, inArray, sql } from 'drizzle-orm' import { recordProjectedUseCaseAuditEntries } from '@/lib/core/application/authorized-workspace-use-case' @@ -96,16 +96,15 @@ export const listProjects: OperationUseCase< JOIN ${workspace} ON ${workspace.organizationId} = ${member.organizationId} WHERE ${member.userId} = ${principal.userId} AND ${inArray(member.role, ORG_ADMIN_ROLES)} ) - SELECT DISTINCT ${projectWorkspace.projectId} AS id + SELECT DISTINCT ${workspace.projectId} AS id FROM accessible - JOIN ${projectWorkspace} ON ${projectWorkspace.workspaceId} = accessible.workspace_id JOIN ${workspace} ON ${workspace.id} = accessible.workspace_id AND ${workspace.archivedAt} IS NULL JOIN ${project} - ON ${project.id} = ${projectWorkspace.projectId} AND ${project.archivedAt} IS NULL + ON ${project.id} = ${workspace.projectId} AND ${project.archivedAt} IS NULL WHERE TRUE ${input.organizationId ? sql`AND ${project.organizationId} = ${input.organizationId}` : sql``} - ${input.cursor ? sql`AND ${projectWorkspace.projectId} > ${input.cursor}` : sql``} + ${input.cursor ? sql`AND ${workspace.projectId} > ${input.cursor}` : sql``} ORDER BY 1 LIMIT ${input.limit + 1} `) @@ -212,11 +211,11 @@ export const getWorkspaceProject: OperationUseCase< await requireProjectApiEnabled() return db.transaction(async (tx) => { const [membership] = await tx - .select({ projectId: projectWorkspace.projectId }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, input.workspaceId)) + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(eq(workspace.id, input.workspaceId)) .limit(1) - if (!membership) throw new OrchestrationError('not_found', 'Project not found') + if (!membership?.projectId) throw new OrchestrationError('not_found', 'Project not found') return { project: presentProject( await authorizeProject( diff --git a/apps/sim/lib/projects/lifecycle.ts b/apps/sim/lib/projects/lifecycle.ts index 2fe08df4890..b75d7e798c7 100644 --- a/apps/sim/lib/projects/lifecycle.ts +++ b/apps/sim/lib/projects/lifecycle.ts @@ -1,4 +1,4 @@ -import { project, projectWorkspace, workspace } from '@sim/db/schema' +import { project, workspace } from '@sim/db/schema' import { asc, eq } from 'drizzle-orm' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbTransaction } from '@/lib/db/types' @@ -20,11 +20,10 @@ export async function archiveProjectInTransaction( const now = record.archivedAt ?? new Date() const environments = await tx .select({ id: workspace.id }) - .from(projectWorkspace) - .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) - .where(eq(projectWorkspace.projectId, projectId)) + .from(workspace) + .where(eq(workspace.projectId, projectId)) .orderBy(asc(workspace.id)) - .for('no key update', { of: workspace }) + .for('no key update') const effects: EnvironmentArchiveEffects[] = [] for (const { id } of environments) effects.push(await archiveEnvironmentInTransaction(tx, id, now)) diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index 2543c23b5f7..b9fec536b3a 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -1,8 +1,8 @@ -import { permissionGroup, project, projectWorkspace, workspace } from '@sim/db/schema' +import { permissionGroup, project, workspace } from '@sim/db/schema' import { getPostgresErrorCode } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { compareStrings, truncateAtCodePoint } from '@sim/utils/string' -import { and, asc, eq, inArray, isNull, notInArray, type SQL, sql } from 'drizzle-orm' +import { and, asc, eq, inArray, isNotNull, isNull, notInArray, type SQL, sql } from 'drizzle-orm' import { OrchestrationError } from '@/lib/core/orchestration/types' import { acquireAdvisoryXactLock, @@ -110,9 +110,9 @@ function generatedProjectName(workspaceName: string): string { function forkSubtree(workspaceId: string): SQL { return sql` WITH RECURSIVE descendants AS ( - SELECT id, name, owner_id, archived_at FROM workspace WHERE id = ${workspaceId} + SELECT id, name, owner_id, archived_at, project_id FROM workspace WHERE id = ${workspaceId} UNION - SELECT w.id, w.name, w.owner_id, w.archived_at + SELECT w.id, w.name, w.owner_id, w.archived_at, w.project_id FROM workspace w JOIN descendants d ON w.forked_from_workspace_id = d.id )` } @@ -142,17 +142,17 @@ export async function lockWorkspaceProject(tx: DbTransaction, workspaceId: strin return withProjectLockTimeout(tx, PROJECT_CHANGING, async () => { await acquireBackfillWriteLocks(tx, [workspaceId]) const [membership] = await tx - .select({ projectId: projectWorkspace.projectId }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, workspaceId)) + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(eq(workspace.id, workspaceId)) .limit(1) - if (!membership) return null + if (!membership?.projectId) return null await acquireAdvisoryXactLock(tx, 'project', projectLockKey(membership.projectId)) const [current] = await tx .select({ project }) - .from(projectWorkspace) - .innerJoin(project, eq(project.id, projectWorkspace.projectId)) - .where(eq(projectWorkspace.workspaceId, workspaceId)) + .from(workspace) + .innerJoin(project, eq(project.id, workspace.projectId)) + .where(eq(workspace.id, workspaceId)) .limit(1) if (!current || current.project.id !== membership.projectId) { throw new ProjectConflictError('Project membership changed; retry the operation') @@ -184,9 +184,9 @@ async function requireUnassignedForkSubtree(tx: DbTransaction, workspaceId: stri const ids = descendants.map((row) => row.id) await lockProjectBackfillWrites(tx, ids) const rows = await tx - .select({ id: projectWorkspace.workspaceId }) - .from(projectWorkspace) - .where(inArray(projectWorkspace.workspaceId, ids)) + .select({ id: workspace.id }) + .from(workspace) + .where(and(inArray(workspace.id, ids), isNotNull(workspace.projectId))) .limit(1) if (rows.length) throw new ProjectConflictError('Fork descendants need Project membership reconciliation') @@ -210,9 +210,8 @@ export async function archiveProjectWithLastEnvironment( eq(project.id, projectId), isNull(project.archivedAt), sql`NOT EXISTS ( - SELECT 1 FROM ${projectWorkspace} - JOIN ${workspace} ON ${workspace.id} = ${projectWorkspace.workspaceId} - WHERE ${projectWorkspace.projectId} = ${projectId} + SELECT 1 FROM ${workspace} + WHERE ${workspace.projectId} = ${projectId} AND ${workspace.id} <> ${workspaceId} AND ${workspace.archivedAt} IS NULL )` @@ -242,9 +241,7 @@ export async function splitForkProject( owner_id: string archived_at: Date | null project_id: string | null - }>(sql`${forkSubtree(workspaceId)} - SELECT d.*, pw.project_id FROM descendants d LEFT JOIN project_workspace pw ON pw.workspace_id = d.id - `) + }>(sql`${forkSubtree(workspaceId)} SELECT * FROM descendants`) if (rows.some((row) => row.project_id !== owner.id)) throw new ProjectConflictError( 'Fork descendants need Project membership reconciliation before disconnecting' @@ -255,11 +252,10 @@ export async function splitForkProject( const ids = rows.map((row) => row.id) const [remaining] = await tx .select({ id: workspace.id }) - .from(projectWorkspace) - .innerJoin(workspace, eq(workspace.id, projectWorkspace.workspaceId)) + .from(workspace) .where( and( - eq(projectWorkspace.projectId, owner.id), + eq(workspace.projectId, owner.id), isNull(workspace.archivedAt), notInArray(workspace.id, ids) ) @@ -284,12 +280,6 @@ export async function splitForkProject( .orderBy(asc(workspace.id)) .for('no key update') await tx.update(workspace).set({ projectId: id }).where(inArray(workspace.id, ids)) - await tx - .update(projectWorkspace) - .set({ projectId: id }) - .where( - and(eq(projectWorkspace.projectId, owner.id), inArray(projectWorkspace.workspaceId, ids)) - ) }) if (owner.organizationId) { await acquirePermissionGroupOrgLock(tx, owner.organizationId) @@ -318,18 +308,18 @@ export async function transferWorkspaceProjects( if (!workspaceIds.length) return await lockProjectBackfillWrites(tx, workspaceIds) const owners = await tx - .selectDistinct({ id: projectWorkspace.projectId }) - .from(projectWorkspace) - .where(inArray(projectWorkspace.workspaceId, workspaceIds)) - .orderBy(asc(projectWorkspace.projectId)) - if (!owners.length) return - const projectIds = owners.map((row) => row.id) + .selectDistinct({ id: workspace.projectId }) + .from(workspace) + .where(inArray(workspace.id, workspaceIds)) + .orderBy(asc(workspace.projectId)) + const projectIds = owners.flatMap((row) => (row.id ? [row.id] : [])) + if (!projectIds.length) return await tryLockProjects(tx, projectIds) const selected = new Set(workspaceIds) const members = await tx - .select({ id: projectWorkspace.workspaceId }) - .from(projectWorkspace) - .where(inArray(projectWorkspace.projectId, projectIds)) + .select({ id: workspace.id }) + .from(workspace) + .where(inArray(workspace.projectId, projectIds)) if (members.some((row) => !selected.has(row.id))) { throw new ProjectConflictError( 'Move all environments in the Project together, or disconnect the fork first' diff --git a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts index 85513c193f5..c52c8fcd0e4 100644 --- a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts +++ b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts @@ -10,7 +10,6 @@ import { outboxEvent, permissions, project, - projectWorkspace, user, userTableDefinitions, workflow, @@ -161,7 +160,6 @@ describe('authorized fork and sync against PostgreSQL', () => { billedAccountUserId: userId, allowPersonalApiKeys: true, }) - await tx.insert(projectWorkspace).values({ projectId, workspaceId: sourceWorkspaceId }) }) await db.insert(permissions).values({ id: generateId(), @@ -195,21 +193,9 @@ describe('authorized fork and sync against PostgreSQL', () => { }) }) afterAll(async () => { - const owned = await db - .select({ id: project.id }) - .from(project) - .where(eq(project.ownerId, userId)) - if (owned.length) { - await db.delete(projectWorkspace).where( - inArray( - projectWorkspace.projectId, - owned.map((row) => row.id) - ) - ) - await db.delete(project).where(eq(project.ownerId, userId)) - } for (const id of createdWorkspaceIds) await db.delete(workspace).where(eq(workspace.id, id)) await db.delete(workspace).where(eq(workspace.id, sourceWorkspaceId)) + await db.delete(project).where(eq(project.ownerId, userId)) await db.delete(user).where(eq(user.id, userId)) await db.$client.end() }) diff --git a/apps/sim/lib/workspaces/create.ts b/apps/sim/lib/workspaces/create.ts index f88aacb9786..ab8d951af88 100644 --- a/apps/sim/lib/workspaces/create.ts +++ b/apps/sim/lib/workspaces/create.ts @@ -1,5 +1,5 @@ import { db } from '@sim/db' -import { permissions, projectWorkspace, type WorkspaceMode, workspace } from '@sim/db/schema' +import { permissions, type WorkspaceMode, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { getPostgresConstraintName, getPostgresErrorCode } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' @@ -142,8 +142,6 @@ export async function createWorkspaceWithProjectInTransaction( updatedAt: now, }) - await tx.insert(projectWorkspace).values({ projectId, workspaceId }) - const permissionRows = [ { id: generateId(), diff --git a/apps/sim/lib/workspaces/organization-workspaces.integration.ts b/apps/sim/lib/workspaces/organization-workspaces.integration.ts index 331b66d33b8..d2a2fa1e916 100644 --- a/apps/sim/lib/workspaces/organization-workspaces.integration.ts +++ b/apps/sim/lib/workspaces/organization-workspaces.integration.ts @@ -26,15 +26,23 @@ const database = drizzle(connection, { schema }) beforeAll(async () => { await connection.unsafe(`CREATE SCHEMA "${schemaName}"`) await connection.unsafe(` - CREATE TABLE project_workspace (project_id text NOT NULL, workspace_id text UNIQUE NOT NULL); CREATE TABLE member (id text PRIMARY KEY, organization_id text, user_id text, role text); CREATE TABLE organization (id text PRIMARY KEY, storage_used_bytes bigint NOT NULL); + CREATE TABLE project ( + id text PRIMARY KEY, owner_id text NOT NULL, + organization_id text REFERENCES organization(id) ON DELETE RESTRICT, + updated_at timestamp + ); + CREATE TABLE permission_group ( + id text PRIMARY KEY, organization_id text, config jsonb, updated_at timestamp + ); CREATE TABLE invitation ( id text PRIMARY KEY, organization_id text REFERENCES organization(id) ON DELETE CASCADE ); CREATE TABLE user_stats (user_id text PRIMARY KEY, storage_used_bytes bigint NOT NULL); CREATE TABLE workspace ( id text PRIMARY KEY, name text, owner_id text, organization_id text, workspace_mode text, + project_id text NOT NULL REFERENCES project(id) ON DELETE RESTRICT, billed_account_user_id text, allow_personal_api_keys boolean DEFAULT true, archived_at timestamp, organization_assigned_at timestamp, updated_at timestamp, storage_used_bytes bigint NOT NULL @@ -56,16 +64,17 @@ beforeAll(async () => { beforeEach(async () => { await connection.unsafe(` - TRUNCATE member, organization, invitation, user_stats, workspace, permissions, + TRUNCATE member, organization, project, permission_group, invitation, user_stats, workspace, permissions, workspace_files, knowledge_base, document, knowledge_connector; INSERT INTO member VALUES ('owner-membership', 'org', 'org-owner', 'owner'); INSERT INTO organization VALUES ('org', 40); + INSERT INTO project (id, owner_id, organization_id) VALUES ('project', 'workspace-owner', 'org'); INSERT INTO invitation VALUES ('invitation', 'org'); INSERT INTO user_stats VALUES ('org-owner', 5); INSERT INTO workspace ( - id, name, owner_id, organization_id, workspace_mode, billed_account_user_id, + id, project_id, name, owner_id, organization_id, workspace_mode, billed_account_user_id, organization_assigned_at, storage_used_bytes - ) VALUES ('workspace', 'Workspace', 'workspace-owner', 'org', 'organization', 'org-owner', now(), 40); + ) VALUES ('workspace', 'project', 'Workspace', 'workspace-owner', 'org', 'organization', 'org-owner', now(), 40); INSERT INTO workspace_files VALUES ('workspace', 'workspace', 40); `) }) @@ -171,6 +180,9 @@ describe('organization workspace detachment lock order', () => { organization_assigned_at: null, storage_used_bytes: 40, }) + expect(await connection`SELECT id, owner_id, organization_id FROM project`).toEqual([ + { id: 'project', owner_id: 'org-owner', organization_id: null }, + ]) expect( await connection`SELECT storage_used_bytes::int FROM organization WHERE id = 'org'` ).toEqual(mode === 'organization-delete' ? [] : [{ storage_used_bytes: 0 }]) diff --git a/packages/db/migrations/0403_workspace_project_column.sql b/packages/db/migrations/0403_workspace_project_column.sql index c93ad61f79b..5d5e5074841 100644 --- a/packages/db/migrations/0403_workspace_project_column.sql +++ b/packages/db/migrations/0403_workspace_project_column.sql @@ -1,5 +1,49 @@ -ALTER TABLE "workspace" ADD COLUMN IF NOT EXISTS "project_id" text; +COMMIT; --> statement-breakpoint +BEGIN; +SET LOCAL transaction_timeout = '5s'; +SET LOCAL statement_timeout = '5s'; +-- Install both directions together, without queuing behind a live writer. +LOCK TABLE workspace IN ACCESS EXCLUSIVE MODE NOWAIT; +LOCK TABLE project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT; +ALTER TABLE "workspace" ADD COLUMN IF NOT EXISTS "project_id" text; + +CREATE OR REPLACE FUNCTION workspace_sync_project_membership_fn() RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + IF TG_OP = 'UPDATE' AND OLD.project_id IS NOT DISTINCT FROM NEW.project_id THEN + RETURN NEW; + END IF; + IF NEW.project_id IS NULL THEN + DELETE FROM project_workspace WHERE workspace_id = NEW.id; + ELSE + INSERT INTO project_workspace (project_id, workspace_id) VALUES (NEW.project_id, NEW.id) + ON CONFLICT (workspace_id) DO UPDATE SET project_id = EXCLUDED.project_id + WHERE project_workspace.project_id IS DISTINCT FROM EXCLUDED.project_id; + END IF; + RETURN NEW; +END; +$$; +CREATE OR REPLACE TRIGGER workspace_sync_project_membership + AFTER INSERT OR UPDATE OF project_id ON workspace + FOR EACH ROW EXECUTE FUNCTION workspace_sync_project_membership_fn(); + +CREATE OR REPLACE FUNCTION project_workspace_sync_column_fn() RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + IF TG_OP = 'DELETE' THEN + UPDATE workspace SET project_id = NULL WHERE id = OLD.workspace_id AND project_id IS NOT NULL; + RETURN OLD; + END IF; + IF TG_OP = 'UPDATE' AND OLD.workspace_id IS DISTINCT FROM NEW.workspace_id THEN + RAISE EXCEPTION 'Project membership workspace identity cannot change' USING ERRCODE = '55000'; + END IF; + UPDATE workspace SET project_id = NEW.project_id + WHERE id = NEW.workspace_id AND project_id IS DISTINCT FROM NEW.project_id; + RETURN NEW; +END; +$$; +CREATE OR REPLACE TRIGGER project_workspace_sync_column + AFTER INSERT OR UPDATE OR DELETE ON project_workspace + FOR EACH ROW EXECUTE FUNCTION project_workspace_sync_column_fn(); COMMIT; --> statement-breakpoint SET statement_timeout = '15min'; @@ -11,6 +55,74 @@ DROP INDEX CONCURRENTLY IF EXISTS "workspace_project_id_id_idx"; --> statement-breakpoint CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_project_id_id_idx" ON "workspace" USING btree ("project_id","id"); --> statement-breakpoint +SET lock_timeout = '1s'; +--> statement-breakpoint +CREATE OR REPLACE PROCEDURE pg_temp.copy_workspace_projects() LANGUAGE plpgsql AS $$ +DECLARE + previous_id text; + batch_ids text[]; + attempts integer := 0; + copied integer := 0; + changed integer; + retry boolean; +BEGIN + LOOP + SELECT array_agg(id ORDER BY id COLLATE "C") INTO batch_ids FROM ( + SELECT w.id FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id + WHERE (previous_id IS NULL OR w.id COLLATE "C" > previous_id COLLATE "C") + AND w.project_id IS DISTINCT FROM pw.project_id + ORDER BY w.id COLLATE "C" LIMIT 100 + ) batch; + EXIT WHEN batch_ids IS NULL; + PERFORM set_config('transaction_timeout', '5s', true); + retry := false; + BEGIN + PERFORM id FROM workspace WHERE id = ANY(batch_ids) ORDER BY id COLLATE "C" + FOR NO KEY UPDATE NOWAIT; + -- Old writers can hold connector rows first: release the entire batch instead of waiting. + PERFORM workspace_id FROM project_workspace WHERE workspace_id = ANY(batch_ids) + ORDER BY workspace_id COLLATE "C" FOR UPDATE NOWAIT; + -- Read again after locking so a concurrent detach cannot be overwritten by discovery's snapshot. + UPDATE workspace w SET project_id = pw.project_id FROM project_workspace pw + WHERE w.id = ANY(batch_ids) AND pw.workspace_id = w.id + AND w.project_id IS DISTINCT FROM pw.project_id; + GET DIAGNOSTICS changed = ROW_COUNT; + copied := copied + changed; + EXCEPTION WHEN lock_not_available OR deadlock_detected OR serialization_failure THEN + retry := true; + END; + COMMIT; + IF retry THEN + attempts := attempts + 1; + IF attempts >= 20 THEN + RAISE EXCEPTION 'Project column copy remains busy; safe to retry migration' + USING ERRCODE = '55P03', DETAIL = batch_ids[1]; + END IF; + PERFORM pg_sleep(0.05 + random() * 0.15); + COMMIT; + ELSE + attempts := 0; + previous_id := batch_ids[cardinality(batch_ids)]; + END IF; + END LOOP; + RAISE NOTICE 'Project column copy complete: % environments copied', copied; +END; +$$; +--> statement-breakpoint +CALL pg_temp.copy_workspace_projects(); +--> statement-breakpoint +DROP PROCEDURE pg_temp.copy_workspace_projects(); +--> statement-breakpoint +DO $$ BEGIN + IF EXISTS ( + SELECT 1 FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id + WHERE w.project_id IS DISTINCT FROM pw.project_id + ) THEN + RAISE EXCEPTION 'Project column and connector assignments disagree; reconcile before retrying' + USING ERRCODE = '55000'; + END IF; +END $$; +--> statement-breakpoint SET lock_timeout = '5s'; --> statement-breakpoint SET statement_timeout = 0; diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 6b196455f1f..3b2f8dd9e19 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2117,13 +2117,16 @@ export const project = pgTable( export const projectWorkspace = pgTable( 'project_workspace', { - // contract-pending(after workspace.projectId backfill and required-column enforcement): retire connector readers/writers, then remove this table in a later deployment once older instances drain. + // contract-pending(after #8830 is fully deployed and pre-8830 servers/workers drain): remove this table and its synchronization triggers in #8590 — application membership reads and writes use workspace.projectId. + /** @deprecated Use workspace.projectId; retained for pre-8830 binaries during rollout. */ projectId: text('project_id') .notNull() .references(() => project.id, { onDelete: 'restrict' }), + /** @deprecated Use workspace.id. */ workspaceId: text('workspace_id') .notNull() .references(() => workspace.id, { onDelete: 'cascade' }), + /** @deprecated Membership is stored on workspace. */ createdAt: timestamp('created_at').notNull().defaultNow(), }, (table) => ({ diff --git a/packages/db/scripts/workspace-project-column.integration.ts b/packages/db/scripts/workspace-project-column.integration.ts index c55a2ba307f..949fe3148fa 100644 --- a/packages/db/scripts/workspace-project-column.integration.ts +++ b/packages/db/scripts/workspace-project-column.integration.ts @@ -1,7 +1,8 @@ import { readFileSync } from 'node:fs' import { applyMigration, withMigrationSchema } from '@sim/db/scripts/migration-fixture' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { getPostgresErrorCode } from '@sim/utils/errors' -import type postgres from 'postgres' +import postgres from 'postgres' import { describe, expect, it } from 'vitest' const migration = readFileSync( @@ -29,8 +30,59 @@ async function fixture(run: (sql: postgres.Sql) => Promise): Promise }) } +async function installBridgeBeforeCopy(sql: postgres.Sql): Promise { + for (const statement of migration.split('--> statement-breakpoint')) { + if (statement.trim().startsWith('CALL pg_temp.copy_workspace_projects()')) return + if (statement.trim()) await sql.unsafe(statement) + } + throw new Error('Migration has no Project copy entry point') +} + +async function withConnections( + sql: postgres.Sql, + run: (writer: postgres.Sql, fork: postgres.Sql, observer: postgres.Sql) => Promise +): Promise { + const [scope] = await sql<{ schema: string }[]>`SELECT current_schema() AS schema` + if (!scope) throw new Error('Migration fixture has no current schema') + const writer = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => {} }) + const fork = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => {} }) + const observer = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => {} }) + try { + for (const connection of [writer, fork, observer]) { + await connection`SET search_path = ${connection(scope.schema)}` + await connection`SET statement_timeout = '10s'` + } + await run(writer, fork, observer) + } finally { + await Promise.all([writer.end(), fork.end(), observer.end()]) + } +} + +async function backendPid(sql: postgres.Sql): Promise { + const [backend] = await sql<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + if (!backend) throw new Error('Migration fixture connection has no backend PID') + return backend.pid +} + +async function expectBlockedBy( + observer: postgres.Sql, + waitingPid: number, + blockingPid: number +): Promise { + await expect + .poll( + async () => { + const [state] = await observer<{ waiting: boolean }[]>` + SELECT ${blockingPid} = ANY(pg_blocking_pids(${waitingPid})) AS waiting` + return state?.waiting + }, + { timeout: 5_000 } + ) + .toBe(true) +} + describe('workspace Project column compatibility migration', () => { - it('preserves existing memberships and permits deployed writers that omit the column', async () => { + it('copies existing memberships and permits old workspace-before-Project creation', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) @@ -44,19 +96,273 @@ describe('workspace Project column compatibility migration', () => { await sql`SELECT w.id, w.project_id, pw.project_id AS membership, w.forked_from_workspace_id AS parent FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` ).toEqual([ - { id: 'old-writer', project_id: null, membership: 'new-project', parent: null }, - { id: 'personal', project_id: null, membership: 'private', parent: null }, - { id: 'production', project_id: null, membership: 'family', parent: null }, - { id: 'staging', project_id: null, membership: 'family', parent: 'production' }, + { id: 'old-writer', project_id: 'new-project', membership: 'new-project', parent: null }, + { id: 'personal', project_id: 'private', membership: 'private', parent: null }, + { id: 'production', project_id: 'family', membership: 'family', parent: null }, + { id: 'staging', project_id: 'family', membership: 'family', parent: 'production' }, + ]) + }) + }) + + it('mirrors old connector reassignment and removal while rejecting workspace identity changes', async () => { + await fixture(async (sql) => { + await applyMigration(sql, migration) + await sql`INSERT INTO workspace (id) VALUES ('moved-target')` + await sql`UPDATE project_workspace SET project_id = 'private' WHERE workspace_id = 'staging'` + expect(await sql`SELECT project_id FROM workspace WHERE id = 'staging'`).toEqual([ + { project_id: 'private' }, + ]) + + await expect(sql`UPDATE project_workspace SET workspace_id = 'moved-target' + WHERE workspace_id = 'staging'`).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55000' + ) + expect( + await sql`SELECT id, project_id FROM workspace WHERE id IN ('staging', 'moved-target') ORDER BY id` + ).toEqual([ + { id: 'moved-target', project_id: null }, + { id: 'staging', project_id: 'private' }, + ]) + expect( + await sql`SELECT project_id, workspace_id FROM project_workspace WHERE workspace_id = 'staging'` + ).toEqual([{ project_id: 'private', workspace_id: 'staging' }]) + + await sql`DELETE FROM project_workspace WHERE workspace_id = 'staging'` + expect(await sql`SELECT project_id FROM workspace WHERE id = 'staging'`).toEqual([ + { project_id: null }, + ]) + expect(await sql`SELECT workspace_id FROM project_workspace ORDER BY workspace_id`).toEqual([ + { workspace_id: 'personal' }, + { workspace_id: 'production' }, + ]) + }) + }) + + it('mirrors new column writes, fork detach and workspace cascades into old readers', async () => { + await fixture(async (sql) => { + await applyMigration(sql, migration) + await sql`INSERT INTO workspace (id, project_id, forked_from_workspace_id) + VALUES ('new-fork', 'family', 'production')` + expect( + await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'new-fork'` + ).toEqual([{ project_id: 'family' }]) + await sql`UPDATE workspace SET project_id = 'private' WHERE id = 'new-fork'` + expect( + await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'new-fork'` + ).toEqual([{ project_id: 'private' }]) + + await sql`UPDATE workspace SET project_id = NULL, forked_from_workspace_id = NULL + WHERE id = 'new-fork'` + expect( + await sql`SELECT workspace_id FROM project_workspace WHERE workspace_id = 'new-fork'` + ).toEqual([]) + expect( + await sql`SELECT project_id, forked_from_workspace_id FROM workspace WHERE id = 'new-fork'` + ).toEqual([{ project_id: null, forked_from_workspace_id: null }]) + + await sql`DELETE FROM workspace WHERE id = 'production'` + expect(await sql`SELECT workspace_id FROM project_workspace ORDER BY workspace_id`).toEqual([ + { workspace_id: 'personal' }, + { workspace_id: 'staging' }, + ]) + expect( + await sql`SELECT project_id, forked_from_workspace_id FROM workspace WHERE id = 'staging'` + ).toEqual([{ project_id: 'family', forked_from_workspace_id: null }]) + }) + }) + + it('rejects missing Projects atomically and rolls back both representations with the writer', async () => { + await fixture(async (sql) => { + await applyMigration(sql, migration) + await expect( + sql`INSERT INTO workspace (id, project_id) VALUES ('valid-new', 'family'), ('invalid-new', 'missing')` + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23503') + expect(await sql`SELECT id FROM workspace WHERE id IN ('valid-new', 'invalid-new')`).toEqual( + [] + ) + expect( + await sql`SELECT workspace_id FROM project_workspace WHERE workspace_id = 'valid-new'` + ).toEqual([]) + + await expect( + sql.begin(async (tx) => { + await tx`UPDATE workspace SET project_id = 'private' WHERE id = 'staging'` + await tx`UPDATE workspace SET project_id = 'missing' WHERE id = 'production'` + }) + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23503') + expect( + await sql`SELECT w.id, w.project_id, pw.project_id AS membership + FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id + WHERE w.id IN ('production', 'staging') ORDER BY w.id` + ).toEqual([ + { id: 'production', project_id: 'family', membership: 'family' }, + { id: 'staging', project_id: 'family', membership: 'family' }, + ]) + }) + }) + + it('replays without restoring a detached fork or overwriting a newer Project assignment', async () => { + await fixture(async (sql) => { + await applyMigration(sql, migration) + await sql`UPDATE workspace SET project_id = 'private' WHERE id = 'production'` + await sql`UPDATE workspace SET project_id = NULL, forked_from_workspace_id = NULL + WHERE id = 'staging'` + + await applyMigration(sql, migration) + await applyMigration(sql, migration) + + expect( + await sql`SELECT w.id, w.project_id, pw.project_id AS membership, w.forked_from_workspace_id AS parent + FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` + ).toEqual([ + { id: 'personal', project_id: 'private', membership: 'private', parent: null }, + { id: 'production', project_id: 'private', membership: 'private', parent: null }, + { id: 'staging', project_id: null, membership: null, parent: null }, + ]) + + await sql`INSERT INTO project_workspace VALUES ('family', 'staging')` + expect(await sql`SELECT project_id FROM workspace WHERE id = 'staging'`).toEqual([ + { project_id: 'family' }, + ]) + }) + }) + + it('commits bounded copy batches, retains completed work on failure and safely resumes', async () => { + await fixture(async (sql) => { + await sql`ALTER TABLE workspace ADD COLUMN project_id text` + await sql`INSERT INTO workspace (id) + SELECT 'batch-' || lpad(n::text, 4, '0') FROM generate_series(1, 251) n` + await sql`INSERT INTO project_workspace (project_id, workspace_id) + SELECT 'family', id FROM workspace WHERE id LIKE 'batch-%'` + await sql`CREATE TABLE copy_commit_audit (workspace_id text PRIMARY KEY, transaction_id bigint NOT NULL)` + await sql.unsafe(`CREATE FUNCTION record_copy_transaction() RETURNS trigger LANGUAGE plpgsql AS $$ + BEGIN + IF NEW.project_id IS NOT NULL AND OLD.project_id IS DISTINCT FROM NEW.project_id THEN + INSERT INTO copy_commit_audit VALUES (NEW.id, txid_current()) ON CONFLICT DO NOTHING; + END IF; + RETURN NEW; + END; + $$`) + await sql`CREATE TRIGGER record_copy_transaction AFTER UPDATE OF project_id ON workspace + FOR EACH ROW EXECUTE FUNCTION record_copy_transaction()` + await sql`ALTER TABLE workspace ADD CONSTRAINT reject_later_copy + CHECK (id <> 'batch-0201' OR project_id IS NULL)` + + await expect(applyMigration(sql, migration)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '23514' + ) + expect( + await sql`SELECT count(*)::int AS count FROM workspace WHERE project_id IS NOT NULL` + ).toEqual([{ count: 200 }]) + expect( + await sql`SELECT count(*)::int AS count FROM copy_commit_audit GROUP BY transaction_id ORDER BY transaction_id` + ).toEqual([{ count: 100 }, { count: 100 }]) + expect(await sql`SELECT project_id FROM workspace WHERE id = 'batch-0201'`).toEqual([ + { project_id: null }, + ]) + + await sql`ALTER TABLE workspace DROP CONSTRAINT reject_later_copy` + await sql`UPDATE project_workspace SET project_id = 'private' WHERE workspace_id = 'batch-0001'` + await sql`DELETE FROM project_workspace WHERE workspace_id = 'batch-0002'` + await applyMigration(sql, migration) + await applyMigration(sql, migration) + + expect( + await sql`SELECT w.id, w.project_id, pw.project_id AS membership FROM workspace w + LEFT JOIN project_workspace pw ON pw.workspace_id = w.id + WHERE w.id IN ('batch-0001', 'batch-0002', 'batch-0201', 'batch-0251') ORDER BY w.id` + ).toEqual([ + { id: 'batch-0001', project_id: 'private', membership: 'private' }, + { id: 'batch-0002', project_id: null, membership: null }, + { id: 'batch-0201', project_id: 'family', membership: 'family' }, + { id: 'batch-0251', project_id: 'family', membership: 'family' }, ]) + expect( + await sql`SELECT count(*)::int AS count FROM workspace w + LEFT JOIN project_workspace pw ON pw.workspace_id = w.id + WHERE w.project_id IS DISTINCT FROM pw.project_id` + ).toEqual([{ count: 0 }]) + expect( + await sql`SELECT count(*)::int AS count FROM copy_commit_audit GROUP BY transaction_id ORDER BY transaction_id` + ).toEqual([{ count: 100 }, { count: 100 }, { count: 54 }]) }) }) + it.each([ + { change: 'legacy reassignment', projectId: 'private' }, + { change: 'legacy removal', projectId: null }, + ])( + 'preserves $change and a waiting fork detach during concurrent copy', + async ({ projectId }) => { + await fixture(async (sql) => { + await installBridgeBeforeCopy(sql) + await sql`UPDATE workspace SET project_id = 'family' WHERE id = 'staging'` + await withConnections(sql, async (writer, fork, observer) => { + const writerPid = await backendPid(writer) + const forkPid = await backendPid(fork) + const copyPid = await backendPid(sql) + let forkLock: Promise | undefined + let copy: Promise | undefined + await writer.unsafe('BEGIN') + await fork.unsafe('BEGIN') + try { + await writer`SELECT pg_advisory_xact_lock(hashtextextended('project:family', 0))` + if (projectId === null) { + await writer`DELETE FROM project_workspace WHERE workspace_id = 'production'` + } else { + await writer`UPDATE project_workspace SET project_id = ${projectId} + WHERE workspace_id = 'production'` + } + forkLock = + fork`SELECT pg_advisory_xact_lock(hashtextextended('project:family', 0))`.then( + () => undefined + ) + void forkLock.catch(() => undefined) + await expectBlockedBy(observer, forkPid, writerPid) + + copy = sql.unsafe('CALL pg_temp.copy_workspace_projects()').then(() => undefined) + void copy.catch(() => undefined) + await expect + .poll( + async () => { + const [activity] = await observer<{ wait_event: string | null }[]>` + SELECT wait_event FROM pg_stat_activity WHERE pid = ${copyPid}` + return activity?.wait_event + }, + { timeout: 5_000 } + ) + .toBe('PgSleep') + + await writer.unsafe('COMMIT') + await forkLock + await fork`UPDATE workspace SET project_id = NULL, forked_from_workspace_id = NULL + WHERE id = 'staging'` + await fork.unsafe('COMMIT') + await copy + + expect( + await observer`SELECT w.id, w.project_id, pw.project_id AS membership, + w.forked_from_workspace_id AS parent FROM workspace w + LEFT JOIN project_workspace pw ON pw.workspace_id = w.id + WHERE w.id IN ('production', 'staging') ORDER BY w.id` + ).toEqual([ + { id: 'production', project_id: projectId, membership: projectId, parent: null }, + { id: 'staging', project_id: null, membership: null, parent: null }, + ]) + } finally { + await writer.unsafe('ROLLBACK') + await forkLock?.catch(() => undefined) + await fork.unsafe('ROLLBACK') + await copy?.catch(() => undefined) + } + }) + }) + } + ) + it('allows old account teardown to delete a Project before its populated workspace', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) - await sql`UPDATE workspace w SET project_id = pw.project_id - FROM project_workspace pw WHERE pw.workspace_id = w.id` await sql.begin(async (tx) => { await tx`DELETE FROM project_workspace WHERE project_id = 'private'` @@ -79,8 +385,6 @@ describe('workspace Project column compatibility migration', () => { it('repairs an interrupted concurrent index build on replay without changing Project assignments', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) - await sql`UPDATE workspace w SET project_id = pw.project_id - FROM project_workspace pw WHERE pw.workspace_id = w.id` await sql`DROP INDEX CONCURRENTLY workspace_project_id_id_idx` await expect( sql`CREATE UNIQUE INDEX CONCURRENTLY workspace_project_id_id_idx ON workspace(project_id)` @@ -105,7 +409,6 @@ describe('workspace Project column compatibility migration', () => { await sql.begin(async (tx) => { await tx`INSERT INTO workspace (id, project_id, forked_from_workspace_id) VALUES ('new-fork', 'family', 'production')` - await tx`INSERT INTO project_workspace VALUES ('family', 'new-fork')` }) expect( await sql`SELECT count(*)::int AS count FROM workspace WHERE project_id = 'family'` From d6b7a5d8f579de912cfa8968cd46090b2f42f2c8 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 20:26:18 -0700 Subject: [PATCH 051/113] test(projects): align transfer mocks with workspace membership --- .../lib/billing/organizations/lock-order.test.ts | 10 ++++++---- apps/sim/lib/workspaces/admin-move.test.ts | 14 ++++---------- .../lib/workspaces/organization-workspaces.test.ts | 2 ++ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/apps/sim/lib/billing/organizations/lock-order.test.ts b/apps/sim/lib/billing/organizations/lock-order.test.ts index b6234a37a9a..4c9edc649ee 100644 --- a/apps/sim/lib/billing/organizations/lock-order.test.ts +++ b/apps/sim/lib/billing/organizations/lock-order.test.ts @@ -181,11 +181,13 @@ describe('workspace payer-change transaction lock ordering', () => { it('locks nonzero workspaces before join billing or aggregate payer changes', async () => { const ops: Array<{ op: 'lock' | 'payer-transfer' | 'update'; table: unknown }> = [] let memberSelectCount = 0 - const rowsForTable = (table: unknown): unknown[] => { + const rowsForTable = (table: unknown, fields?: Record): unknown[] => { if (table === workspace) { + if (fields?.id === workspace.projectId) return [{ id: null }] return [ { id: 'workspace-1', + projectId: null, billedAccountUserId: 'user-1', organizationId: null, storageUsedBytes: 128, @@ -203,7 +205,7 @@ describe('workspace payer-change transaction lock ordering', () => { if (table === userStats) return [{ currentPeriodCost: '5' }] return [] } - const select = () => { + const select = (fields?: Record) => { let table: unknown const chain = { from(source: unknown) { @@ -220,9 +222,9 @@ describe('workspace payer-change transaction lock ordering', () => { ops.push({ op: 'lock', table }) return chain }, - limit: async () => rowsForTable(table), + limit: async () => rowsForTable(table, fields), then(resolve: (rows: unknown[]) => unknown, reject: (error: unknown) => unknown) { - return Promise.resolve(rowsForTable(table)).then(resolve, reject) + return Promise.resolve(rowsForTable(table, fields)).then(resolve, reject) }, } return chain diff --git a/apps/sim/lib/workspaces/admin-move.test.ts b/apps/sim/lib/workspaces/admin-move.test.ts index 5336e79af5c..a9cfebf1c8c 100644 --- a/apps/sim/lib/workspaces/admin-move.test.ts +++ b/apps/sim/lib/workspaces/admin-move.test.ts @@ -191,20 +191,13 @@ const destination = { } /** - * The move flow reads the workspace three times in order: the optimistic - * pre-transaction organization read that decides which organizations to lock, - * the locked classification row, and the final summary reload. The workspace - * queue therefore gets one set per read, in that order. - * - * Keep this comment in step with the reads — a stale count silently shifts - * every later queue entry onto the wrong statement, which surfaces as an - * unrelated "could not be reloaded" failure rather than a queueing error. - * - * All invitation/grant/permission selects resolve the queue-less empty default. + * Queues the optimistic scope, locked workspace, optional unassigned Project projection, + * and summary reads. An already completed move skips the Project transfer. */ function queueMoveSelects(workspaceRow: Record) { queueTableRows(workspace, [workspaceRow]) queueTableRows(workspace, [workspaceRow]) + if (workspaceRow.organizationId !== destination.id) queueTableRows(workspace, [{ id: null }]) queueTableRows(workspace, [workspaceRow]) queueTableRows(organization, [destination]) } @@ -670,6 +663,7 @@ describe('moveWorkspaceToOrganization retries', () => { queueTableRows(workspace, [organizationWorkspace]) queueTableRows(workspace, [{ ...organizationWorkspace, organizationId: 'org-moved' }]) queueTableRows(workspace, [{ ...organizationWorkspace, organizationId: 'org-moved' }]) + queueTableRows(workspace, [{ id: null }]) queueTableRows(workspace, [{ ...organizationWorkspace, organizationId: 'org-moved' }]) queueTableRows(organization, [destination]) diff --git a/apps/sim/lib/workspaces/organization-workspaces.test.ts b/apps/sim/lib/workspaces/organization-workspaces.test.ts index eb919215c51..34f0eae8f5f 100644 --- a/apps/sim/lib/workspaces/organization-workspaces.test.ts +++ b/apps/sim/lib/workspaces/organization-workspaces.test.ts @@ -191,6 +191,7 @@ describe('organization workspace helpers', () => { { id: 'ws-1', ownerId: 'creator-1', billedAccountUserId: 'old-owner' }, ]) queueTableRows(schemaMock.invitation, [{ id: 'invite-pending' }, { id: 'invite-terminal' }]) + queueTableRows(schemaMock.workspace, [{ id: null }]) queueTableRows(schemaMock.workspace, [{ id: 'ws-1' }]) const result = @@ -328,6 +329,7 @@ describe('organization workspace helpers', () => { { id: 'ws-2', ownerId: 'creator-2', billedAccountUserId: 'old-owner' }, ]) queueTableRows(schemaMock.invitation, [{ id: 'invite-2' }]) + queueTableRows(schemaMock.workspace, [{ id: null }]) queueTableRows(schemaMock.workspace, [{ id: 'ws-2' }]) const result = await detachOrganizationWorkspaces('org-1') From 137616788821a762db58f8ee62b9246126d24276 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 20:29:21 -0700 Subject: [PATCH 052/113] test(projects): provision required membership in transfer unit fixtures --- .../billing/organizations/lock-order.test.ts | 8 +++++--- apps/sim/lib/workspaces/admin-move.test.ts | 18 ++++++++++++++---- .../workspaces/organization-workspaces.test.ts | 17 ++++++++++++++--- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/apps/sim/lib/billing/organizations/lock-order.test.ts b/apps/sim/lib/billing/organizations/lock-order.test.ts index 4c9edc649ee..adf7a26fdb9 100644 --- a/apps/sim/lib/billing/organizations/lock-order.test.ts +++ b/apps/sim/lib/billing/organizations/lock-order.test.ts @@ -11,6 +11,7 @@ import { organization, outboxEvent, permissions, + project, subscription as subscriptionTable, user, userStats, @@ -183,17 +184,18 @@ describe('workspace payer-change transaction lock ordering', () => { let memberSelectCount = 0 const rowsForTable = (table: unknown, fields?: Record): unknown[] => { if (table === workspace) { - if (fields?.id === workspace.projectId) return [{ id: null }] + if (fields?.id === workspace.projectId) return [{ id: 'project-1' }] return [ { id: 'workspace-1', - projectId: null, + projectId: 'project-1', billedAccountUserId: 'user-1', organizationId: null, storageUsedBytes: 128, }, ] } + if (table === project) return [{ id: 'project-1', organizationId: null }] if (table === permissions) return [{ userId: 'user-1' }] if (table === member) { memberSelectCount += 1 @@ -230,7 +232,7 @@ describe('workspace payer-change transaction lock ordering', () => { return chain } const tx = { - execute: async () => [], + execute: async () => [{ acquired: true }], select, selectDistinct: select, insert: () => ({ diff --git a/apps/sim/lib/workspaces/admin-move.test.ts b/apps/sim/lib/workspaces/admin-move.test.ts index a9cfebf1c8c..dc226c249e7 100644 --- a/apps/sim/lib/workspaces/admin-move.test.ts +++ b/apps/sim/lib/workspaces/admin-move.test.ts @@ -4,6 +4,7 @@ import { member, organization, outboxEvent, + project, subscription, workspace, } from '@sim/db/schema' @@ -154,6 +155,7 @@ vi.mock('@/lib/workspaces/admin-move-source-impact', () => ({ const movedWorkspace = { id: 'workspace-1', + projectId: 'project-1', name: 'Already moved', ownerId: 'workspace-owner', ownerName: 'Workspace Owner', @@ -191,13 +193,18 @@ const destination = { } /** - * Queues the optimistic scope, locked workspace, optional unassigned Project projection, - * and summary reads. An already completed move skips the Project transfer. + * Queues the optimistic scope, locked workspace, Project transfer and summary reads. An already completed move skips the Project transfer. */ function queueMoveSelects(workspaceRow: Record) { queueTableRows(workspace, [workspaceRow]) queueTableRows(workspace, [workspaceRow]) - if (workspaceRow.organizationId !== destination.id) queueTableRows(workspace, [{ id: null }]) + if (workspaceRow.organizationId !== destination.id) { + queueTableRows(workspace, [{ id: workspaceRow.projectId }]) + queueTableRows(workspace, [{ id: workspaceRow.id }]) + queueTableRows(project, [ + { id: workspaceRow.projectId, organizationId: workspaceRow.organizationId }, + ]) + } queueTableRows(workspace, [workspaceRow]) queueTableRows(organization, [destination]) } @@ -230,6 +237,7 @@ afterAll(resetDbChainMock) beforeEach(() => { resetDbChainMock() + dbChainMockFns.execute.mockResolvedValue([{ acquired: true }]) isInvitationExpired.mockReturnValue(false) /** * `vi.clearAllMocks` clears call records but keeps implementations, so a @@ -663,7 +671,9 @@ describe('moveWorkspaceToOrganization retries', () => { queueTableRows(workspace, [organizationWorkspace]) queueTableRows(workspace, [{ ...organizationWorkspace, organizationId: 'org-moved' }]) queueTableRows(workspace, [{ ...organizationWorkspace, organizationId: 'org-moved' }]) - queueTableRows(workspace, [{ id: null }]) + queueTableRows(workspace, [{ id: organizationWorkspace.projectId }]) + queueTableRows(workspace, [{ id: organizationWorkspace.id }]) + queueTableRows(project, [{ id: organizationWorkspace.projectId, organizationId: 'org-moved' }]) queueTableRows(workspace, [{ ...organizationWorkspace, organizationId: 'org-moved' }]) queueTableRows(organization, [destination]) diff --git a/apps/sim/lib/workspaces/organization-workspaces.test.ts b/apps/sim/lib/workspaces/organization-workspaces.test.ts index 34f0eae8f5f..b8457acffa2 100644 --- a/apps/sim/lib/workspaces/organization-workspaces.test.ts +++ b/apps/sim/lib/workspaces/organization-workspaces.test.ts @@ -54,6 +54,7 @@ idMockFns.mockGenerateShortId.mockReturnValue('short-id') describe('organization workspace helpers', () => { beforeEach(() => { resetDbChainMock() + dbChainMockFns.execute.mockResolvedValue([{ acquired: true }]) mockEnsureUserInOrganizationTx.mockReset() mockChangeWorkspaceStoragePayersInTx.mockReset() mockSyncUsageLimitsFromSubscription.mockResolvedValue(undefined) @@ -191,7 +192,9 @@ describe('organization workspace helpers', () => { { id: 'ws-1', ownerId: 'creator-1', billedAccountUserId: 'old-owner' }, ]) queueTableRows(schemaMock.invitation, [{ id: 'invite-pending' }, { id: 'invite-terminal' }]) - queueTableRows(schemaMock.workspace, [{ id: null }]) + queueTableRows(schemaMock.workspace, [{ id: 'project-1' }]) + queueTableRows(schemaMock.workspace, [{ id: 'ws-1' }]) + queueTableRows(schemaMock.project, [{ id: 'project-1', organizationId: 'org-1' }]) queueTableRows(schemaMock.workspace, [{ id: 'ws-1' }]) const result = @@ -245,7 +248,13 @@ describe('organization workspace helpers', () => { organizationAssignedAt: null, }) ) - expect(dbChainMockFns.update).toHaveBeenCalledTimes(1) + expect(dbChainMockFns.update.mock.calls.map(([table]) => table)).toEqual([ + schemaMock.project, + schemaMock.workspace, + ]) + expect(dbChainMockFns.set).toHaveBeenCalledWith( + expect.objectContaining({ organizationId: null, ownerId: 'owner-1' }) + ) expect(dbChainMockFns.insert).toHaveBeenCalledTimes(1) expect(dbChainMockFns.values).toHaveBeenCalledWith([ expect.objectContaining({ entityId: 'ws-1', userId: 'owner-1' }), @@ -329,7 +338,9 @@ describe('organization workspace helpers', () => { { id: 'ws-2', ownerId: 'creator-2', billedAccountUserId: 'old-owner' }, ]) queueTableRows(schemaMock.invitation, [{ id: 'invite-2' }]) - queueTableRows(schemaMock.workspace, [{ id: null }]) + queueTableRows(schemaMock.workspace, [{ id: 'project-2' }]) + queueTableRows(schemaMock.workspace, [{ id: 'ws-2' }]) + queueTableRows(schemaMock.project, [{ id: 'project-2', organizationId: 'org-1' }]) queueTableRows(schemaMock.workspace, [{ id: 'ws-2' }]) const result = await detachOrganizationWorkspaces('org-1') From 031ec268011fc41a452c5696fced8e49529a1701 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 21:05:09 -0700 Subject: [PATCH 053/113] improvement(workspaces): present API responses explicitly --- apps/sim/app/api/workspaces/[id]/route.ts | 6 ++--- apps/sim/lib/api/contracts/workspaces.ts | 12 ++++++++++ apps/sim/lib/workspaces/list.ts | 6 ++--- apps/sim/lib/workspaces/response.ts | 29 +++++++++++++++++++++++ 4 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 apps/sim/lib/workspaces/response.ts diff --git a/apps/sim/app/api/workspaces/[id]/route.ts b/apps/sim/app/api/workspaces/[id]/route.ts index 355cf4a492e..c00d5831e10 100644 --- a/apps/sim/app/api/workspaces/[id]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/route.ts @@ -1,7 +1,6 @@ import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit' import { workflow } from '@sim/db/schema' import { createLogger } from '@sim/logger' -import { omit } from '@sim/utils/object' import { and, eq, isNull } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { deleteWorkspaceBodySchema, updateWorkspaceContract } from '@/lib/api/contracts' @@ -11,6 +10,7 @@ import { getSession } from '@/lib/auth' import { changeWorkspaceStoragePayerInTx } from '@/lib/billing/storage/payer-transfer' import { captureServerEvent } from '@/lib/posthog/server' import { archiveWorkspace } from '@/lib/workspaces/lifecycle' +import { presentWorkspace } from '@/lib/workspaces/response' const logger = createLogger('WorkspaceByIdAPI') @@ -52,7 +52,7 @@ export const GET = withRouteHandler( return NextResponse.json({ workspace: { - ...omit(workspaceDetails, ['projectId']), + ...presentWorkspace(workspaceDetails), permissions: userPermission, }, }) @@ -217,7 +217,7 @@ export const PATCH = withRouteHandler( return NextResponse.json({ workspace: { - ...omit(updatedWorkspace, ['projectId']), + ...(updatedWorkspace ? presentWorkspace(updatedWorkspace) : {}), permissions: userPermission, }, }) diff --git a/apps/sim/lib/api/contracts/workspaces.ts b/apps/sim/lib/api/contracts/workspaces.ts index b7f03ef2826..bbfc2b2e924 100644 --- a/apps/sim/lib/api/contracts/workspaces.ts +++ b/apps/sim/lib/api/contracts/workspaces.ts @@ -13,6 +13,7 @@ export type WorkspacePermission = z.output export const workspaceSchema = z.object({ id: z.string(), name: z.string(), + color: z.string().optional(), logoUrl: z.string().nullable().optional(), ownerId: z.string(), organizationId: z.string().nullable(), @@ -28,6 +29,17 @@ export const workspaceSchema = z.object({ isOrgAdmin: z.boolean().optional(), billedAccountUserId: z.string().nullable().optional(), allowPersonalApiKeys: z.boolean().optional(), + storageUsedBytes: z.number().nonnegative().optional(), + inboxEnabled: z.boolean().optional(), + inboxAddress: z.string().nullable().optional(), + inboxProviderId: z.string().nullable().optional(), + inboxSecretScope: z.string().optional(), + inboxMountedSecrets: z.array(z.string()).optional(), + archivedAt: z.string().datetime().nullable().optional(), + organizationAssignedAt: z.string().datetime().nullable().optional(), + forkSyncNewWorkflowsExcluded: z.boolean().optional(), + createdAt: z.string().datetime().optional(), + updatedAt: z.string().datetime().optional(), inviteMembersEnabled: z.boolean().optional(), inviteDisabledReason: z.string().nullable().optional(), inviteUpgradeRequired: z.boolean().optional(), diff --git a/apps/sim/lib/workspaces/list.ts b/apps/sim/lib/workspaces/list.ts index c7c91c044d7..88f9094a99a 100644 --- a/apps/sim/lib/workspaces/list.ts +++ b/apps/sim/lib/workspaces/list.ts @@ -1,7 +1,6 @@ import { db } from '@sim/db' import { pinnedItem, settings, type workspace as workspaceTable } from '@sim/db/schema' import type { PermissionType } from '@sim/platform-authz/workspace' -import { omit } from '@sim/utils/object' import { and, eq } from 'drizzle-orm' import type { PlanCategory } from '@/lib/billing/plan-helpers' import { @@ -14,13 +13,14 @@ import { type WorkspaceCreationPolicy, type WorkspaceInviteFlags, } from '@/lib/workspaces/policy' +import { presentWorkspace } from '@/lib/workspaces/response' import { listAccessibleWorkspaceRowsForUser, type WorkspaceScope } from '@/lib/workspaces/utils' import { listRecentWorkspaceIds, sortByVisitRecency } from '@/lib/workspaces/visits' type WorkspaceRow = typeof workspaceTable.$inferSelect /** Accessible workspace row decorated with the viewer's role and invite policy flags. */ -export type WorkspaceWithInviteFlags = Omit & +export type WorkspaceWithInviteFlags = ReturnType & WorkspaceInviteFlags & { role: 'owner' | 'admin' | 'member' permissions: PermissionType @@ -95,7 +95,7 @@ async function buildWorkspacesWithInviteFlags( const invitePolicy = evaluateWorkspaceInvitePolicy(workspaceDetails, { billedPlanCategory }) return { - ...omit(workspaceDetails, ['projectId']), + ...presentWorkspace(workspaceDetails), role: workspaceDetails.ownerId === userId ? ('owner' as const) diff --git a/apps/sim/lib/workspaces/response.ts b/apps/sim/lib/workspaces/response.ts new file mode 100644 index 00000000000..af33bf6fea8 --- /dev/null +++ b/apps/sim/lib/workspaces/response.ts @@ -0,0 +1,29 @@ +import type { workspace } from '@sim/db/schema' +import type { Workspace } from '@/lib/api/contracts/workspaces' + +/** Projects internal workspace metadata without exposing additional storage columns. */ +export function presentWorkspace(row: typeof workspace.$inferSelect) { + return { + id: row.id, + name: row.name, + color: row.color, + logoUrl: row.logoUrl, + ownerId: row.ownerId, + organizationId: row.organizationId, + workspaceMode: row.workspaceMode, + billedAccountUserId: row.billedAccountUserId, + storageUsedBytes: row.storageUsedBytes, + allowPersonalApiKeys: row.allowPersonalApiKeys, + inboxEnabled: row.inboxEnabled, + inboxAddress: row.inboxAddress, + inboxProviderId: row.inboxProviderId, + inboxSecretScope: row.inboxSecretScope, + inboxMountedSecrets: row.inboxMountedSecrets, + archivedAt: row.archivedAt?.toISOString() ?? null, + organizationAssignedAt: row.organizationAssignedAt?.toISOString() ?? null, + forkedFromWorkspaceId: row.forkedFromWorkspaceId, + forkSyncNewWorkflowsExcluded: row.forkSyncNewWorkflowsExcluded, + createdAt: row.createdAt.toISOString(), + updatedAt: row.updatedAt.toISOString(), + } satisfies Workspace +} From 3e4c88c2c425e33168478c486baa3fef40779d4e Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 23:22:42 -0700 Subject: [PATCH 054/113] feat(projects): prepare legacy membership with a resumable operator tool --- .../backfill-repair.integration.ts | 136 +++++ apps/sim/lib/projects/backfill-repair.ts | 99 ++++ apps/sim/lib/workflows/lifecycle.ts | 16 +- apps/sim/lib/workspaces/lifecycle.ts | 20 +- apps/sim/scripts/backfill-projects.ts | 466 +++++++++++++++++ packages/db/maintenance/project-backfill.ts | 383 ++++++++++++++ .../0405_project_membership_enforcement.sql | 143 +---- .../db/scripts/project-contract-required.ts | 9 + .../scripts/project-contract.integration.ts | 487 +++++++++++------- 9 files changed, 1420 insertions(+), 339 deletions(-) create mode 100644 apps/sim/lib/projects/__integration__/backfill-repair.integration.ts create mode 100644 apps/sim/lib/projects/backfill-repair.ts create mode 100644 apps/sim/scripts/backfill-projects.ts create mode 100644 packages/db/maintenance/project-backfill.ts diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts new file mode 100644 index 00000000000..d1380f6b029 --- /dev/null +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -0,0 +1,136 @@ +import { execFile } from 'node:child_process' +import { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { promisify } from 'node:util' +import journal from '@sim/db/migrations/meta/_journal.json' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { generateId } from '@sim/utils/id' +import postgres from 'postgres' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' + +const admin = postgres(readTestDatabaseUrl(), { max: 1 }) +const name = `project_repair_test_${generateId().replaceAll('-', '')}` +const url = new URL(readTestDatabaseUrl()) +url.pathname = `/${name}` +const client = postgres(url.toString(), { max: 1, onnotice: () => {} }) +const directory = await mkdtemp(join(tmpdir(), 'project-repair-test-')) +const manifest = join(directory, 'manifest.json') +const report = join(directory, 'report.json') +const environment = { + ...process.env, + DATABASE_URL: url.toString(), + MIGRATION_DATABASE_URL: url.toString(), +} +const execute = promisify(execFile) +const run = (command: string) => + execute( + 'bun', + [ + '--no-env-file', + 'scripts/backfill-projects.ts', + command, + '--manifest', + manifest, + ...(command === 'plan' ? [] : ['--report', report, '--ack-release-drained']), + ], + { + cwd: new URL('../../../', import.meta.url), + env: environment, + timeout: 45000, + } + ) + +beforeAll(async () => { + await admin.unsafe(`CREATE DATABASE "${name}"`) + await client.unsafe( + 'CREATE EXTENSION vector; CREATE EXTENSION btree_gin; CREATE EXTENSION pg_trgm' + ) + const folder = join(directory, 'migrations') + await mkdir(join(folder, 'meta'), { recursive: true }) + const expansion = journal.entries.find((entry) => entry.tag === '0404_workspace_project_column') + if (!expansion) throw new Error('Missing expansion migration') + const entries = journal.entries.filter((entry) => entry.when <= expansion.when) + const source = new URL('../../../../../packages/db/migrations/', import.meta.url) + for (const entry of entries) + await copyFile(new URL(`${entry.tag}.sql`, source), join(folder, `${entry.tag}.sql`)) + await writeFile(join(folder, 'meta/_journal.json'), JSON.stringify({ ...journal, entries })) + await execute( + 'bun', + [ + '--no-env-file', + '-e', + ` + import postgres from 'postgres'; + import { drizzle } from 'drizzle-orm/postgres-js'; + import { migrate } from 'drizzle-orm/postgres-js/migrator'; + import { runScriptMigrations } from '@sim/db/script-migrations/index'; + const sql = postgres(process.env.MIGRATION_DATABASE_URL,{max:1,onnotice:()=>{}}); + try { await migrate(drizzle(sql),{migrationsFolder:process.env.FIXTURE_MIGRATIONS}); await runScriptMigrations(sql); } + finally { await sql.end(); } + `, + ], + { + cwd: new URL('../../../../../packages/db/', import.meta.url), + env: { ...environment, FIXTURE_MIGRATIONS: folder }, + timeout: 120000, + } + ) +}, 120000) + +afterAll(async () => { + await client.end({ timeout: 2 }) + await admin.unsafe(`DROP DATABASE IF EXISTS "${name}" WITH (FORCE)`) + await admin.end() + await rm(directory, { recursive: true, force: true }) +}) + +describe('Operator archive repair against the full compatible schema', () => { + it('commits the complete cascade, reports external failure and resumes cleanup from its original manifest', async () => { + await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) VALUES ('owner','Owner','repair@fixture.test',true,now(),now())` + await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,archived_at) VALUES ('env','Archived','owner','owner','2026-05-01')` + await client`INSERT INTO workflow (id,name,user_id,workspace_id,last_synced,created_at,updated_at,is_deployed,is_public_api) + VALUES ('flow','Flow','owner','env',now(),now(),now(),true,true)` + await client`INSERT INTO workflow_schedule (id,workflow_id,trigger_type,status,next_run_at,last_queued_at) VALUES ('schedule','flow','schedule','active',now(),now())` + await client`INSERT INTO webhook (id,workflow_id,provider,path,provider_config) VALUES ('hook','flow','gitlab','fixture-hook','{}')` + await client`INSERT INTO chat (id,workflow_id,user_id,identifier,title) VALUES ('chat','flow','owner','repair-chat','Chat')` + await client`INSERT INTO workflow_deployment_version (id,workflow_id,version,state,is_active) VALUES ('deployment','flow',1,'{}',true)` + await client`INSERT INTO workflow_mcp_server (id,workspace_id,created_by,name,is_public) VALUES ('server','env','owner','Server',true)` + await client`INSERT INTO workflow_mcp_tool (id,server_id,workflow_id,tool_name) VALUES ('tool','server','flow','fixture')` + await run('plan') + await expect(run('repair')).rejects.toMatchObject({ code: 1 }) + expect(JSON.parse(await readFile(report, 'utf8'))).toMatchObject({ + status: 'failed', + repairsCompleted: [], + }) + expect(await client`SELECT archived_at::text,is_deployed,is_public_api FROM workflow`).toEqual([ + { archived_at: '2026-05-01 00:00:00', is_deployed: false, is_public_api: false }, + ]) + expect(await client`SELECT status,next_run_at,last_queued_at FROM workflow_schedule`).toEqual([ + { status: 'disabled', next_run_at: null, last_queued_at: null }, + ]) + expect( + await client`SELECT id FROM webhook WHERE archived_at IS NULL OR is_active` + ).toHaveLength(0) + expect(await client`SELECT id FROM chat WHERE archived_at IS NULL OR is_active`).toHaveLength(0) + expect(await client`SELECT id FROM workflow_deployment_version WHERE is_active`).toHaveLength(0) + expect(await client`SELECT id FROM workflow_mcp_tool WHERE archived_at IS NULL`).toHaveLength(0) + expect( + await client`SELECT id FROM workflow_mcp_server WHERE deleted_at IS NULL OR is_public` + ).toHaveLength(0) + expect(await client`SELECT id FROM workspace WHERE project_id IS NULL`).toHaveLength(1) + await expect(run('verify')).rejects.toMatchObject({ code: 2 }) + /** The fixture's missing provider credentials are remediated without making an external request. */ + await client`UPDATE webhook SET provider = 'generic'` + await expect(run('repair')).rejects.toMatchObject({ code: 2 }) + expect(JSON.parse(await readFile(report, 'utf8'))).toMatchObject({ repairsCompleted: ['env'] }) + await run('apply') + await run('verify') + expect(await client`SELECT archived_at::text FROM project`).toEqual([ + { archived_at: '2026-05-01 00:00:00' }, + ]) + expect(await client`SELECT project_id FROM workspace`).toEqual( + await client`SELECT project_id FROM project_workspace` + ) + }, 120000) +}) diff --git a/apps/sim/lib/projects/backfill-repair.ts b/apps/sim/lib/projects/backfill-repair.ts new file mode 100644 index 00000000000..1df1fe7ffb3 --- /dev/null +++ b/apps/sim/lib/projects/backfill-repair.ts @@ -0,0 +1,99 @@ +import { + type ProjectArchiveRepair, + ProjectBackfillBusy, + ProjectBackfillConflict, +} from '@sim/db/maintenance/project-backfill' +import * as schema from '@sim/db/schema' +import { and, eq, inArray, isNull, sql } from 'drizzle-orm' +import { drizzle } from 'drizzle-orm/postgres-js' +import type { Sql } from 'postgres' +import { tryAcquireAdvisoryXactLocks } from '@/lib/db/advisory-locks' +import { + archiveEnvironmentInTransaction, + type EnvironmentArchiveEffects, + finishEnvironmentArchive, +} from '@/lib/workspaces/lifecycle' + +/** + * Repairs reviewed legacy archives before membership enforcement. The immutable manifest retains + * workflow identities before commit, so reruns can finish provider cleanup after a process crash. + */ +export async function repairArchivedProjectEnvironment( + client: Sql, + repair: ProjectArchiveRepair, + requestId: string +): Promise { + const connection = drizzle({ client, schema }) + const effects = await connection.transaction(async (tx) => { + await tx.execute(sql`SET LOCAL transaction_timeout = '5s'`) + await tx.execute(sql`SET LOCAL statement_timeout = '3s'`) + await tx.execute(sql`SET LOCAL lock_timeout = '250ms'`) + if ( + !(await tryAcquireAdvisoryXactLocks(tx, 'project_backfill', [ + `project-backfill:${repair.workspaceId}`, + ])) + ) + throw new ProjectBackfillBusy('Archive repair workspace is busy') + const [membership] = await tx + .select({ projectId: schema.workspace.projectId }) + .from(schema.workspace) + .where(eq(schema.workspace.id, repair.workspaceId)) + if ( + membership?.projectId && + !(await tryAcquireAdvisoryXactLocks(tx, 'project', [`project:${membership.projectId}`])) + ) + throw new ProjectBackfillBusy('Archive repair Project is busy') + const [current] = await tx + .select({ archivedAt: sql`${schema.workspace.archivedAt}::text` }) + .from(schema.workspace) + .where(eq(schema.workspace.id, repair.workspaceId)) + .for('no key update', { noWait: true }) + if (!current || current.archivedAt !== repair.archivedAt) + throw new ProjectBackfillConflict('Reviewed archive changed; rediscover') + const active = await tx + .select({ id: schema.workflow.id }) + .from(schema.workflow) + .where( + and(eq(schema.workflow.workspaceId, repair.workspaceId), isNull(schema.workflow.archivedAt)) + ) + if (active.some((row) => !repair.workflowIds.includes(row.id))) + throw new ProjectBackfillConflict('New workflow found in reviewed archive; rediscover') + const moved = await tx + .select({ id: schema.workflow.id }) + .from(schema.workflow) + .where( + and( + inArray(schema.workflow.id, repair.workflowIds), + sql`${schema.workflow.workspaceId} IS DISTINCT FROM ${repair.workspaceId}` + ) + ) + .limit(1) + if (moved.length) throw new ProjectBackfillConflict('Reviewed workflow moved; rediscover') + await archiveEnvironmentInTransaction( + tx, + repair.workspaceId, + new Date(`${repair.archivedAt.replace(' ', 'T')}Z`) + ) + const tools = await tx + .select({ + workflowId: schema.workflowMcpTool.workflowId, + serverId: schema.workflowMcpTool.serverId, + }) + .from(schema.workflowMcpTool) + .where(inArray(schema.workflowMcpTool.workflowId, repair.workflowIds)) + const servers = await tx + .select({ id: schema.workflowMcpServer.id }) + .from(schema.workflowMcpServer) + .where(eq(schema.workflowMcpServer.workspaceId, repair.workspaceId)) + const result: EnvironmentArchiveEffects = { + workspaceId: repair.workspaceId, + workflows: repair.workflowIds.map((id) => ({ + id, + serverIds: tools.filter((tool) => tool.workflowId === id).map((tool) => tool.serverId), + })), + serverIds: servers.map((server) => server.id), + } + return result + }) + await finishEnvironmentArchive(effects, requestId, { strictExternalCleanup: true }) +} diff --git a/apps/sim/lib/workflows/lifecycle.ts b/apps/sim/lib/workflows/lifecycle.ts index 69ecc8afca0..db50c33047b 100644 --- a/apps/sim/lib/workflows/lifecycle.ts +++ b/apps/sim/lib/workflows/lifecycle.ts @@ -29,6 +29,7 @@ interface ArchiveWorkflowOptions { requestId: string notifySocket?: boolean archivedAt?: Date + strictExternalCleanup?: boolean } async function notifyWorkflowArchived(workflowId: string, requestId: string): Promise { @@ -54,7 +55,8 @@ async function notifyWorkflowArchived(workflowId: string, requestId: string): Pr async function cleanupExternalWebhooksForWorkflow( workflowId: string, - requestId: string + requestId: string, + strict = false ): Promise { try { const { cleanupExternalWebhook } = await import('@/lib/webhooks/provider-subscriptions') @@ -73,8 +75,11 @@ async function cleanupExternalWebhooksForWorkflow( for (const webhookData of webhooksToCleanup) { try { - await cleanupExternalWebhook(webhookData.webhook, webhookData.workflow, requestId) + await cleanupExternalWebhook(webhookData.webhook, webhookData.workflow, requestId, { + throwOnError: strict, + }) } catch (error) { + if (strict) throw error logger.warn( `[${requestId}] Failed to cleanup external webhook ${webhookData.webhook.id} for workflow ${workflowId}`, { error } @@ -82,6 +87,7 @@ async function cleanupExternalWebhooksForWorkflow( } } } catch (error) { + if (strict) throw error logger.warn(`[${requestId}] Error during external webhook cleanup for workflow ${workflowId}`, { error, }) @@ -406,7 +412,11 @@ export async function finishWorkflowArchive( await notifyWorkflowArchived(workflowId, options.requestId) } - await cleanupExternalWebhooksForWorkflow(workflowId, options.requestId) + await cleanupExternalWebhooksForWorkflow( + workflowId, + options.requestId, + options.strictExternalCleanup + ) if (workspaceId && mcpPubSub) { for (const serverId of new Set(serverIds)) { diff --git a/apps/sim/lib/workspaces/lifecycle.ts b/apps/sim/lib/workspaces/lifecycle.ts index d6c88cb377c..d58b18c9296 100644 --- a/apps/sim/lib/workspaces/lifecycle.ts +++ b/apps/sim/lib/workspaces/lifecycle.ts @@ -135,19 +135,25 @@ export async function archiveEnvironmentInTransaction( } /** - * Announces a committed environment archive. Every step is best-effort and isolated, so - * one failed notification never skips the rest. + * Announces a committed environment archive. Cleanup is best-effort by default; maintenance + * callers can require provider cleanup to succeed before recording completion. */ export async function finishEnvironmentArchive( effects: EnvironmentArchiveEffects, - requestId: string + requestId: string, + options: { strictExternalCleanup?: boolean } = {} ): Promise { const { workspaceId } = effects await mapWithConcurrency(effects.workflows, ARCHIVE_NOTIFICATION_CONCURRENCY, (row) => - finishWorkflowArchive(row.id, workspaceId, row.serverIds, { requestId }).catch((error) => - logger.warn(`[${requestId}] Post-archive notification failed for workflow ${row.id}`, { - error, - }) + finishWorkflowArchive(row.id, workspaceId, row.serverIds, { + requestId, + strictExternalCleanup: options.strictExternalCleanup, + }).catch((error) => + options.strictExternalCleanup + ? Promise.reject(error) + : logger.warn(`[${requestId}] Post-archive notification failed for workflow ${row.id}`, { + error, + }) ) ) await mcpService.clearCache(workspaceId).catch(() => undefined) diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts new file mode 100644 index 00000000000..314173aab0d --- /dev/null +++ b/apps/sim/scripts/backfill-projects.ts @@ -0,0 +1,466 @@ +#!/usr/bin/env bun +import { createHash } from 'node:crypto' +import { link, open, readFile, rename, stat, unlink } from 'node:fs/promises' +import { resolve } from 'node:path' +import { parseArgs } from 'node:util' +import { + assertProjectBackfillDatabase, + assignProjectBackfillBatch, + discoverProjectBackfill, + ProjectBackfillBusy, + ProjectBackfillConflict, + projectBackfillDatabaseId, + verifyProjectBackfill, +} from '@sim/db/maintenance/project-backfill' +import { withUtcTimestamps } from '@sim/db/timestamps' +import { createLogger } from '@sim/logger' +import { describeError, getTransientDatabaseFailure } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { generateId } from '@sim/utils/id' +import { backoffWithJitter } from '@sim/utils/retry' +import postgres from 'postgres' +import { z } from 'zod' + +const logger = createLogger('ProjectBackfillOperator', { enabled: true, logLevel: 'INFO' }) +const text = z.string().min(1).max(1024) +const member = z + .object({ + id: text, + parentId: text.nullable(), + ownerId: text, + organizationId: text.nullable(), + archivedAt: text.nullable(), + projectId: text.nullable(), + }) + .strict() +const manifestSchema = z + .object({ + version: z.literal(1), + databaseId: text, + createdAt: text, + families: z + .array(z.object({ rootId: text, members: z.array(member).min(1).max(1000) }).strict()) + .max(250000), + repairs: z + .array( + z + .object({ + workspaceId: text, + archivedAt: text, + workflowIds: z.array(text).min(1).max(1000), + }) + .strict() + ) + .max(250000), + conflicts: z.array(z.object({ id: text, reason: text }).strict()).max(250000), + }) + .strict() +const progressSchema = z + .object({ + version: z.literal(1), + manifestHash: text, + databaseId: text, + codeHash: text, + runId: text, + nextIndex: z.number().int().min(0), + deferred: z.array(z.number().int().min(0)), + repairsCompleted: z.array(text), + assigned: z.number().int().min(0), + projectsCreated: z.number().int().min(0), + alreadyAssigned: z.number().int().min(0), + batches: z.number().int().min(0), + problems: z.array(z.object({ id: text, reason: text })), + status: z.enum(['running', 'paused', 'incomplete', 'complete', 'failed']), + updatedAt: text, + }) + .strict() + +async function readJson(path: string): Promise { + if ((await stat(path)).size > 128 * 1024 * 1024) + throw new Error('Backfill artifact exceeds 128 MiB') + return JSON.parse(await readFile(path, 'utf8')) +} + +async function writeJson(path: string, value: unknown, replace = true): Promise { + const temporary = `${path}.${generateId()}.partial` + const file = await open(temporary, 'wx', 0o600) + try { + await file.writeFile(`${JSON.stringify(value)}\n`) + await file.sync() + } finally { + await file.close() + } + try { + if (replace) await rename(temporary, path) + else await link(temporary, path) + } finally { + await unlink(temporary).catch((error: unknown) => { + if (!(error instanceof Error) || !('code' in error) || error.code !== 'ENOENT') throw error + }) + } +} + +function boundedInteger(value: string | undefined, fallback: number, max: number): number { + const result = value === undefined ? fallback : Number(value) + if (!Number.isSafeInteger(result) || result < 1 || result > max) + throw new Error(`Expected integer between 1 and ${max}`) + return result +} + +async function main() { + const { positionals, values } = parseArgs({ + args: process.argv.slice(2), + allowPositionals: true, + options: { + manifest: { type: 'string' }, + report: { type: 'string' }, + 'batch-size': { type: 'string' }, + 'max-batches': { type: 'string' }, + seconds: { type: 'string' }, + 'pause-ms': { type: 'string' }, + 'ack-release-drained': { type: 'boolean', default: false }, + help: { type: 'boolean', default: false }, + }, + }) + if (values.help || !positionals.length) { + process.stdout.write(`Operator-controlled Project preparation; never runs automatically on deployment.\n +From apps/sim: bun --no-env-file scripts/backfill-projects.ts [options]\n +plan Read-only discovery; atomically writes a new --manifest PATH (never overwrites).\nrepair Repair only archived environments listed in --manifest; requires --report PATH.\napply Assign reviewed families from --manifest; requires --report PATH.\nverify Fresh database validation; include --manifest and --report to verify repair completion.\nstatus Read --report; does not connect or resume.\n +Writes require MIGRATION_DATABASE_URL pointing directly to the primary and --ack-release-drained.\nDeploy #8830 and verify old servers/workers drained first. Keep the reviewed manifest and report\nin durable private job storage. Review conflicts and repairs before repair/apply. Repair requires\nthe app runtime environment (DATABASE_URL must target the same database) for provider cleanup.\n +Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (1–3600), --pause-ms 100 (1–60000).\nA budget limits scheduling; an in-flight transaction has a separate 5s ceiling. Reuse the same report\nto resume; lost reports can be recreated safely from the same manifest. Changed families need a\nnew plan/report. Complete verify plus deployment evidence is required before #8590 enforcement.\nExit: 0 complete, 2 paused/incomplete, 1 failure. Never run db:migrate to launch this tool.\n`) + return + } + const [command] = positionals + if ( + positionals.length !== 1 || + !['plan', 'repair', 'apply', 'verify', 'status'].includes(command) + ) + throw new Error('Unknown command; use --help') + const manifestPath = values.manifest ? resolve(values.manifest) : null + const reportPath = values.report ? resolve(values.report) : null + if (manifestPath && manifestPath === reportPath) + throw new Error('Manifest and report must be different files') + if (command === 'status') { + if (!reportPath) throw new Error('status requires --report') + logger.info('Project backfill status', progressSchema.parse(await readJson(reportPath))) + return + } + const rawUrl = process.env.MIGRATION_DATABASE_URL + if (!rawUrl) throw new Error('MIGRATION_DATABASE_URL is required; no application DSN fallback') + const url = new URL(rawUrl) + if (!['postgres:', 'postgresql:'].includes(url.protocol) || !url.pathname.slice(1)) + throw new Error('Invalid PostgreSQL connection') + const databaseId = projectBackfillDatabaseId(rawUrl) + const live = command === 'repair' || command === 'apply' + if (live && !values['ack-release-drained']) + throw new Error('Verify #8830 server/worker drainage and pass --ack-release-drained') + const batchSize = boundedInteger(values['batch-size'], 50, 50) + const maxBatches = boundedInteger(values['max-batches'], 100, 10000) + const seconds = boundedInteger(values.seconds, 60, 3600) + const pauseMs = boundedInteger(values['pause-ms'], 100, 60000) + const sql = postgres( + rawUrl, + withUtcTimestamps({ + max: 1, + idle_timeout: 0, + max_lifetime: null, + connect_timeout: 10, + onnotice: () => {}, + connection: { + application_name: 'sim-project-backfill', + statement_timeout: 10000, + lock_timeout: 250, + }, + }) + ) + let stop = false + const requestStop = () => { + stop = true + } + process.on('SIGINT', requestStop) + process.on('SIGTERM', requestStop) + let lockPid: number | undefined + let repairRuntimeLoaded = false + async function assertLock() { + const [row] = await sql`SELECT pg_backend_pid() AS pid, EXISTS (SELECT 1 FROM pg_locks + WHERE pid = pg_backend_pid() AND locktype = 'advisory' AND granted + AND classid = ((hashtextextended('sim:project-backfill-operator',0) >> 32) & 4294967295)::oid + AND objid = (hashtextextended('sim:project-backfill-operator',0) & 4294967295)::oid + AND objsubid = 1) AS held` + if (row.pid !== lockPid || !row.held) + throw new Error('Operator session lock was lost; stop and resume with a fresh connection') + } + try { + await assertProjectBackfillDatabase(sql, live) + if (command === 'plan') { + if (!manifestPath) throw new Error('plan requires --manifest') + const manifest = await discoverProjectBackfill(sql, databaseId) + await writeJson(manifestPath, manifest, false) + logger.info('Reviewable Project plan written', { + databaseId, + manifestPath, + families: manifest.families.length, + repairs: manifest.repairs.length, + conflicts: manifest.conflicts.length, + }) + if (manifest.conflicts.length) process.exitCode = 2 + return + } + if (command === 'verify') { + const counts = await verifyProjectBackfill(sql) + if (manifestPath || reportPath) { + if (!manifestPath || !reportPath) + throw new Error('Repair verification needs both --manifest and --report') + const manifest = manifestSchema.parse(await readJson(manifestPath)) + const report = progressSchema.parse(await readJson(reportPath)) + const hash = createHash('sha256').update(JSON.stringify(manifest)).digest('hex') + if ( + manifest.databaseId !== databaseId || + report.databaseId !== databaseId || + report.manifestHash !== hash + ) + throw new Error('Verification artifacts do not match this database and manifest') + counts.pendingCleanup = manifest.repairs.filter( + (repair) => !report.repairsCompleted.includes(repair.workspaceId) + ).length + } + logger.info('Project verification', { databaseId, ...counts }) + if (Object.values(counts).some((count) => count !== 0)) process.exitCode = 2 + return + } + if (!manifestPath || !reportPath) throw new Error('Writes require --manifest and --report') + const manifest = manifestSchema.parse(await readJson(manifestPath)) + if (manifest.databaseId !== databaseId) throw new Error('Manifest belongs to another database') + const manifestHash = createHash('sha256').update(JSON.stringify(manifest)).digest('hex') + const codeHash = createHash('sha256') + .update(await readFile(import.meta.filename)) + .update( + await readFile( + new URL('../../../packages/db/maintenance/project-backfill.ts', import.meta.url) + ) + ) + .update(await readFile(new URL('../lib/projects/backfill-repair.ts', import.meta.url))) + .digest('hex') + let progress: z.infer + try { + progress = progressSchema.parse(await readJson(reportPath)) + } catch (error) { + if (!(error instanceof Error) || !('code' in error) || error.code !== 'ENOENT') throw error + progress = { + version: 1, + manifestHash, + databaseId, + codeHash, + runId: generateId(), + nextIndex: 0, + deferred: [], + repairsCompleted: [], + assigned: 0, + alreadyAssigned: 0, + projectsCreated: 0, + batches: 0, + problems: [], + status: 'running', + updatedAt: new Date().toISOString(), + } + } + if (progress.manifestHash !== manifestHash || progress.databaseId !== databaseId) + throw new Error('Report does not match the reviewed manifest') + if ( + progress.nextIndex > manifest.families.length || + progress.deferred.some((index) => index >= manifest.families.length) + ) + throw new Error('Invalid report cursor') + const [lock] = + await sql`SELECT pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator',0)) AS held, pg_backend_pid() AS pid` + if (!lock.held) throw new Error('Another Project preparation or enforcement runner is active') + lockPid = lock.pid + progress.runId = generateId() + progress.codeHash = codeHash + progress.status = 'running' + const started = performance.now() + let attempts = 0 + let currentBatchSize = batchSize + const pending = [ + ...new Set(progress.deferred), + ...Array.from( + { length: manifest.families.length - progress.nextIndex }, + (_, i) => progress.nextIndex + i + ), + ] + const singleRetry = new Set() + const retryCounts = new Map() + const deferred = new Set(progress.deferred) + const completedRepairs = new Set(progress.repairsCompleted) + async function checkpoint() { + progress.updatedAt = new Date().toISOString() + progress.deferred = [...deferred].sort((a, b) => a - b) + progress.repairsCompleted = [...completedRepairs] + await writeJson(reportPath as string, progress) + } + logger.info('Project operator run started', { + command, + databaseId, + runId: progress.runId, + codeHash, + batchSize, + maxBatches, + seconds, + }) + await checkpoint() + try { + if (command === 'repair') { + if ( + !process.env.DATABASE_URL || + projectBackfillDatabaseId(process.env.DATABASE_URL) !== databaseId + ) + throw new Error( + 'Repair requires DATABASE_URL and MIGRATION_DATABASE_URL to identify the same database' + ) + repairRuntimeLoaded = true + const { repairArchivedProjectEnvironment } = await import('@/lib/projects/backfill-repair') + for (const repair of manifest.repairs) { + if (completedRepairs.has(repair.workspaceId)) continue + if (stop || attempts >= maxBatches || performance.now() - started >= seconds * 1000) break + await assertLock() + attempts++ + await repairArchivedProjectEnvironment(sql, repair, progress.runId) + await assertLock() + completedRepairs.add(repair.workspaceId) + await checkpoint() + logger.info('Archive repair completed', { + workspaceId: repair.workspaceId, + workflows: repair.workflowIds.length, + }) + await sleep(pauseMs) + } + } else { + for (let position = 0; position < pending.length; ) { + if (stop || attempts >= maxBatches || performance.now() - started >= seconds * 1000) break + await assertLock() + const indices = [pending[position++]] + if (manifest.families[indices[0]].members.length === 1 && !singleRetry.has(indices[0])) { + while ( + indices.length < currentBatchSize && + position < pending.length && + manifest.families[pending[position]].members.length === 1 && + !singleRetry.has(pending[position]) + ) + indices.push(pending[position++]) + } + attempts++ + const batchStarted = performance.now() + try { + const result = await assignProjectBackfillBatch( + sql, + indices.map((index) => manifest.families[index]) + ) + await assertLock() + progress.assigned += result.assigned + progress.alreadyAssigned += result.alreadyAssigned + progress.projectsCreated += result.projectsCreated + progress.batches++ + if (performance.now() - batchStarted > 250 && currentBatchSize > 1) { + currentBatchSize = Math.max(1, Math.floor(currentBatchSize / 2)) + logger.warn('Reducing Project batch size after a slow transaction', { + currentBatchSize, + }) + } + for (const index of indices) deferred.delete(index) + progress.problems = progress.problems.filter( + (problem) => !indices.some((index) => manifest.families[index].rootId === problem.id) + ) + logger.info('Project batch committed', { + runId: progress.runId, + ...result, + elapsedMs: performance.now() - batchStarted, + totalAssigned: progress.assigned, + batches: progress.batches, + }) + } catch (error) { + const transient = getTransientDatabaseFailure(error) + if ( + !(error instanceof ProjectBackfillBusy) && + !(error instanceof ProjectBackfillConflict) && + !transient + ) + throw error + await assertLock() + for (const index of indices) { + deferred.add(index) + if (error instanceof ProjectBackfillConflict) { + const id = manifest.families[index].rootId + progress.problems = progress.problems.filter((problem) => problem.id !== id) + progress.problems.push({ id, reason: error.message }) + } + } + if (indices.length > 1) { + for (const index of indices) singleRetry.add(index) + pending.splice(position, 0, ...indices) + } else if (!(error instanceof ProjectBackfillConflict)) { + const index = indices[0] + const retries = (retryCounts.get(index) ?? 0) + 1 + retryCounts.set(index, retries) + if (retries < 3) pending.push(index) + } + logger.warn('Project batch deferred', { + roots: indices.map((index) => manifest.families[index].rootId), + failure: transient ?? (error instanceof Error ? error.name : 'unknown'), + error: describeError(error), + }) + await sleep(backoffWithJitter(1, null, { baseMs: 250, maxMs: 1000 })) + } + progress.nextIndex = Math.max(progress.nextIndex, ...indices.map((index) => index + 1)) + await checkpoint() + await sleep(pauseMs) + } + } + const counts = await verifyProjectBackfill(sql) + const pendingRepairs = manifest.repairs.filter( + (repair) => !completedRepairs.has(repair.workspaceId) + ).length + const workLeft = + progress.nextIndex < manifest.families.length || deferred.size > 0 || pendingRepairs > 0 + progress.status = + !workLeft && + !manifest.conflicts.length && + Object.values(counts).every((count) => count === 0) + ? 'complete' + : stop || attempts >= maxBatches || performance.now() - started >= seconds * 1000 + ? 'paused' + : 'incomplete' + await checkpoint() + logger.info('Project operator run finished', { + ...progress, + problems: progress.problems.length, + deferred: deferred.size, + repairsCompleted: completedRepairs.size, + elapsedMs: performance.now() - started, + verification: counts, + }) + if (progress.status !== 'complete') process.exitCode = 2 + } catch (error) { + progress.status = 'failed' + await checkpoint() + throw error + } + } finally { + process.off('SIGINT', requestStop) + process.off('SIGTERM', requestStop) + await sql.end({ timeout: 5 }) + if (repairRuntimeLoaded) { + const { mcpPubSub } = await import('@/lib/mcp/pubsub') + const { closeRedisConnection } = await import('@/lib/core/config/redis') + const { db, dbReplica } = await import('@sim/db') + mcpPubSub?.dispose() + await closeRedisConnection() + await Promise.all( + [...new Set([db.$client, dbReplica.$client])].map((client) => client.end({ timeout: 5 })) + ) + } + } +} +await main().catch((error) => { + logger.error('Project operator run failed', { error: describeError(error) }) + process.exitCode = 1 +}) diff --git a/packages/db/maintenance/project-backfill.ts b/packages/db/maintenance/project-backfill.ts new file mode 100644 index 00000000000..a2655e3a4f5 --- /dev/null +++ b/packages/db/maintenance/project-backfill.ts @@ -0,0 +1,383 @@ +import { createHash } from 'node:crypto' +import { createLogger } from '@sim/logger' +import { generateId } from '@sim/utils/id' +import { compareStrings, truncateAtCodePoint } from '@sim/utils/string' +import type { Sql, TransactionSql } from 'postgres' + +const logger = createLogger('ProjectBackfill') +const MAX_WORKSPACES = 250_000 +const MAX_FAMILY = 1_000 + +interface ProjectBackfillWorkspace { + id: string + parentId: string | null + ownerId: string + organizationId: string | null + archivedAt: string | null + projectId: string | null +} + +interface ProjectBackfillFamily { + rootId: string + members: ProjectBackfillWorkspace[] +} + +export interface ProjectArchiveRepair { + workspaceId: string + archivedAt: string + workflowIds: string[] +} + +interface ProjectBackfillManifest { + version: 1 + databaseId: string + createdAt: string + families: ProjectBackfillFamily[] + repairs: ProjectArchiveRepair[] + conflicts: { id: string; reason: string }[] +} + +/** A changed plan or ambiguous legacy state requires new discovery, never a guessed merge. */ +export class ProjectBackfillConflict extends Error { + readonly code = '55000' +} + +/** Contention releases the whole transaction before the controller defers the work. */ +export class ProjectBackfillBusy extends Error {} + +/** Non-secret identity binds reviewed intent to one database endpoint and database name. */ +export function projectBackfillDatabaseId(url: string): string { + const target = new URL(url) + return createHash('sha256') + .update(`${target.hostname.toLowerCase()}:${target.port || '5432'}${target.pathname}`) + .digest('hex') +} + +/** Checks physical prerequisites without trusting a local migration journal or a CLI flag. */ +export async function assertProjectBackfillDatabase(sql: Sql, write: boolean): Promise { + const [state] = await sql` + SELECT pg_is_in_recovery() AS replica, current_setting('transaction_read_only') AS readonly, + current_setting('server_version_num')::int AS version, + EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' + AND table_name = 'workspace' AND column_name = 'project_id') AS expanded + ` + if (!state?.expanded) throw new Error('Deploy #8830 before Project preparation') + if (state.version < 170000) throw new Error('Project preparation requires PostgreSQL 17+') + if (write && (state.replica || state.readonly === 'on')) + throw new Error('Project writes require the primary database') +} + +/** Bounded discovery materializes only identity/scope metadata, never workflow content. */ +export async function discoverProjectBackfill( + sql: Sql, + databaseId: string +): Promise { + const rows: ProjectBackfillWorkspace[] = [] + let after = '' + for (;;) { + const page = await sql` + SELECT id, forked_from_workspace_id AS "parentId", owner_id AS "ownerId", + organization_id AS "organizationId", archived_at::text AS "archivedAt", project_id AS "projectId" + FROM workspace WHERE id COLLATE "C" > ${after} COLLATE "C" ORDER BY id COLLATE "C" LIMIT 1000 + ` + if (!page.length) break + rows.push(...page) + if (rows.length > MAX_WORKSPACES) + throw new Error('Project discovery exceeds the workspace limit') + after = page[page.length - 1].id + } + const manifest: ProjectBackfillManifest = { + version: 1, + databaseId, + createdAt: new Date().toISOString(), + families: [], + repairs: [], + conflicts: [], + } + const byId = new Map(rows.map((row) => [row.id, row])) + const rootById = new Map() + const groups = new Map() + for (const row of rows) { + let current: ProjectBackfillWorkspace | undefined = row + const path = new Set() + let root: string | null = null + while (current) { + if (rootById.has(current.id)) { + root = rootById.get(current.id) ?? null + break + } + if (path.has(current.id)) break + path.add(current.id) + if (!current.parentId) { + root = current.id + break + } + current = byId.get(current.parentId) + } + for (const id of path) rootById.set(id, root) + if (!root) { + manifest.conflicts.push({ id: row.id, reason: 'Fork cycle or missing parent' }) + continue + } + const group = groups.get(root) + if (group) group.push(row) + else groups.set(root, [row]) + } + for (const [rootId, members] of groups) { + if (members.every((row) => row.projectId !== null)) continue + const family = { rootId, members } + try { + validateFamily(family) + manifest.families.push(family) + } catch (error) { + if (!(error instanceof ProjectBackfillConflict)) throw error + manifest.conflicts.push({ id: rootId, reason: error.message }) + } + } + manifest.families.sort((a, b) => compareStrings(a.rootId, b.rootId)) + let afterWorkflow = '' + const repairs = new Map() + for (;;) { + const page = await sql<{ id: string; workspaceId: string; archivedAt: string }[]>` + SELECT f.id, w.id AS "workspaceId", w.archived_at::text AS "archivedAt" + FROM workflow f JOIN workspace w ON w.id = f.workspace_id + WHERE w.archived_at IS NOT NULL AND f.archived_at IS NULL + AND f.id COLLATE "C" > ${afterWorkflow} COLLATE "C" + ORDER BY f.id COLLATE "C" LIMIT 1000 + ` + if (!page.length) break + for (const row of page) { + const repair = repairs.get(row.workspaceId) ?? { + workspaceId: row.workspaceId, + archivedAt: row.archivedAt, + workflowIds: [], + } + repair.workflowIds.push(row.id) + if (repair.workflowIds.length > MAX_FAMILY) + throw new Error(`Archive repair exceeds workflow limit: ${row.workspaceId}`) + repairs.set(row.workspaceId, repair) + } + afterWorkflow = page[page.length - 1].id + if (repairs.size > MAX_WORKSPACES) throw new Error('Too many archive repairs') + } + manifest.repairs = [...repairs.values()].sort((a, b) => + compareStrings(a.workspaceId, b.workspaceId) + ) + logger.info('Project discovery finished', { + workspaces: rows.length, + families: manifest.families.length, + repairs: manifest.repairs.length, + conflicts: manifest.conflicts.length, + }) + return manifest +} + +function validateFamily(family: ProjectBackfillFamily): void { + const root = family.members.find((row) => row.id === family.rootId) + if (!root || root.parentId || family.members.length > MAX_FAMILY) + throw new ProjectBackfillConflict('Invalid or oversized fork family') + const members = new Map(family.members.map((row) => [row.id, row])) + if (members.size !== family.members.length) + throw new ProjectBackfillConflict('Duplicate family member') + for (const row of family.members) { + let current = row + const visited = new Set() + while (current.id !== root.id) { + if (visited.has(current.id)) throw new ProjectBackfillConflict('Cyclic family') + visited.add(current.id) + const parent = current.parentId ? members.get(current.parentId) : undefined + if (!parent) throw new ProjectBackfillConflict('Disconnected family member') + current = parent + } + } + if (family.members.some((row) => row.organizationId !== root.organizationId)) + throw new ProjectBackfillConflict('Fork family spans organizations') + if (!root.organizationId && family.members.some((row) => row.ownerId !== root.ownerId)) + throw new ProjectBackfillConflict( + 'Personal family has multiple lifecycle owners; select an owner before rediscovery' + ) + if (new Set(family.members.map((row) => row.projectId).filter(Boolean)).size > 1) + throw new ProjectBackfillConflict('Fork family spans Projects') +} + +/** All lock keys match deployed writers; nonblocking acquisition avoids lock-order inversions. */ +export async function tryProjectBackfillLocks(tx: TransactionSql, keys: string[]): Promise { + const [result] = await tx` + SELECT bool_and(pg_try_advisory_xact_lock(hashtextextended(key, 0))) AS acquired + FROM unnest(${keys}::text[]) AS key /*lock='project_backfill'*/ + ` + if (!result?.acquired) throw new ProjectBackfillBusy('Project backfill target is busy') +} + +/** Writes at most 50 independent families, or one complete fork family, in a bounded transaction. */ +export async function assignProjectBackfillBatch(sql: Sql, families: ProjectBackfillFamily[]) { + if ( + !families.length || + families.length > 50 || + (families.length > 1 && families.some((f) => f.members.length !== 1)) + ) + throw new Error('Expected 1–50 singletons or one complete fork family') + for (const family of families) validateFamily(family) + return sql.begin(async (tx) => { + await tx`SET LOCAL transaction_timeout = '5s'` + await tx`SET LOCAL statement_timeout = '3s'` + await tx`SET LOCAL lock_timeout = '250ms'` + const roots = families.map((f) => f.rootId).sort(compareStrings) + const expected = families.flatMap((f) => f.members).sort((a, b) => compareStrings(a.id, b.id)) + const ids = expected.map((row) => row.id) + if (new Set(ids).size !== ids.length) + throw new ProjectBackfillConflict('Duplicate workspace in batch') + await tryProjectBackfillLocks( + tx, + roots.map((id) => `fork-lineage:${id}`) + ) + await tryProjectBackfillLocks( + tx, + ids.map((id) => `project-backfill:${id}`) + ) + const before = await tx<{ projectId: string }[]>`SELECT project_id AS "projectId" FROM workspace + WHERE id = ANY(${ids}) AND project_id IS NOT NULL GROUP BY project_id ORDER BY project_id COLLATE "C"` + if (before.length) + await tryProjectBackfillLocks( + tx, + before.map((row) => `project:${row.projectId}`) + ) + const current = await tx<(ProjectBackfillWorkspace & { name: string })[]>` + SELECT id, name, forked_from_workspace_id AS "parentId", owner_id AS "ownerId", + organization_id AS "organizationId", archived_at::text AS "archivedAt", project_id AS "projectId" + FROM workspace WHERE id = ANY(${ids}) ORDER BY id COLLATE "C" FOR NO KEY UPDATE NOWAIT + ` + if (current.length !== expected.length) + throw new ProjectBackfillConflict('A reviewed workspace disappeared; rediscover') + const byId = new Map(current.map((row) => [row.id, row])) + for (const row of expected) { + const actual = byId.get(row.id) + if ( + !actual || + actual.parentId !== row.parentId || + actual.ownerId !== row.ownerId || + actual.organizationId !== row.organizationId || + actual.archivedAt !== row.archivedAt || + (row.projectId !== null && actual.projectId !== row.projectId) + ) + throw new ProjectBackfillConflict( + 'Reviewed workspace scope, owner, archive state or assignment changed; rediscover' + ) + } + const children = + await tx`SELECT id FROM workspace WHERE forked_from_workspace_id = ANY(${ids}) AND NOT id = ANY(${ids}) LIMIT 1` + if (children.length) + throw new ProjectBackfillConflict('Reviewed fork family gained descendants; rediscover') + const invalidArchive = + await tx`SELECT f.id FROM workflow f JOIN workspace w ON w.id = f.workspace_id + WHERE w.id = ANY(${ids}) AND w.archived_at IS NOT NULL AND f.archived_at IS NULL LIMIT 1` + if (invalidArchive.length) + throw new ProjectBackfillConflict('Archive repair is required before assignment') + const inserts: { + id: string + name: string + owner_id: string + organization_id: string | null + archived_at: string | null + }[] = [] + const assignments: { id: string; project_id: string }[] = [] + let alreadyAssigned = 0 + for (const family of families) { + const root = byId.get(family.rootId) + if (!root) throw new ProjectBackfillConflict('Root disappeared') + const members = family.members + .map((row) => byId.get(row.id)) + .filter((row) => row !== undefined) + const projectIds = [ + ...new Set(members.map((row) => row.projectId).filter((id): id is string => id !== null)), + ] + if (projectIds.length > 1) throw new ProjectBackfillConflict('Family now spans Projects') + const archiveAt = members.every((row) => row.archivedAt !== null) + ? (members + .map((row) => row.archivedAt ?? '') + .sort(compareStrings) + .at(-1) ?? null) + : null + let projectId = projectIds[0] + if (projectId) { + const [existing] = + await tx`SELECT owner_id, organization_id, archived_at::text FROM project WHERE id = ${projectId} FOR NO KEY UPDATE NOWAIT` + if ( + !existing || + existing.organization_id !== root.organizationId || + (existing.archived_at === null) !== (archiveAt === null) || + (!root.organizationId && existing.owner_id !== root.ownerId) + ) + throw new ProjectBackfillConflict( + 'Existing Project has incompatible ownership, scope or archive state' + ) + if ( + members.some((row) => row.projectId === null) && + ( + await tx`SELECT id FROM workspace WHERE project_id = ${projectId} AND NOT id = ANY(${ids}) LIMIT 1` + ).length + ) + throw new ProjectBackfillConflict( + 'Partial assignment references a Project outside the reviewed family' + ) + } else { + projectId = generateId() + inserts.push({ + id: projectId, + name: `${truncateAtCodePoint(root.name.trim() || 'Untitled', 90, '')} - Project`, + owner_id: root.ownerId, + organization_id: root.organizationId, + archived_at: archiveAt, + }) + } + for (const member of members) { + if (member.projectId) alreadyAssigned++ + else assignments.push({ id: member.id, project_id: projectId }) + } + } + if (inserts.length) + await tx`INSERT INTO project (id,name,owner_id,organization_id,archived_at) + SELECT id,name,owner_id,organization_id,archived_at::timestamp FROM jsonb_to_recordset(${tx.json(inserts)}::jsonb) + AS p(id text,name text,owner_id text,organization_id text,archived_at text)` + let assigned = 0 + if (assignments.length) { + const result = await tx`UPDATE workspace w SET project_id = p.project_id + FROM jsonb_to_recordset(${tx.json(assignments)}::jsonb) AS p(id text, project_id text) + WHERE w.id = p.id AND w.project_id IS NULL RETURNING w.id` + assigned = result.length + if (assigned !== assignments.length) + throw new ProjectBackfillConflict('Assignment changed while locked') + } + return { assigned, alreadyAssigned, projectsCreated: inserts.length } + }) +} + +/** Fresh whole-database validation is independent of manifest pagination and progress counters. */ +export async function verifyProjectBackfill(sql: Sql): Promise> { + const [counts] = await sql[]>` + SELECT + (SELECT count(*)::int FROM workspace WHERE project_id IS NULL) AS unassigned, + (SELECT count(*)::int FROM workspace w LEFT JOIN project p ON p.id = w.project_id + WHERE w.project_id IS NOT NULL AND (p.id IS NULL OR w.organization_id IS DISTINCT FROM p.organization_id)) AS scope, + (SELECT count(*)::int FROM workspace w LEFT JOIN workspace parent ON parent.id = w.forked_from_workspace_id + WHERE w.forked_from_workspace_id IS NOT NULL AND (parent.id IS NULL OR parent.project_id IS DISTINCT FROM w.project_id)) AS lineage, + (SELECT count(*)::int FROM project p WHERE NOT EXISTS (SELECT 1 FROM workspace w WHERE w.project_id = p.id) + OR (p.archived_at IS NULL AND NOT EXISTS (SELECT 1 FROM workspace w WHERE w.project_id = p.id AND w.archived_at IS NULL)) + OR (p.archived_at IS NOT NULL AND EXISTS (SELECT 1 FROM workspace w WHERE w.project_id = p.id AND w.archived_at IS NULL))) AS projects, + (SELECT count(*)::int FROM workflow f JOIN workspace w ON w.id = f.workspace_id + LEFT JOIN project p ON p.id = w.project_id WHERE f.archived_at IS NULL AND (w.archived_at IS NOT NULL OR p.archived_at IS NOT NULL)) AS archive, + (SELECT count(*)::int FROM project p LEFT JOIN "user" u ON u.id = p.owner_id + LEFT JOIN organization o ON o.id = p.organization_id WHERE u.id IS NULL OR (p.organization_id IS NOT NULL AND o.id IS NULL)) AS owners, + (WITH RECURSIVE reachable(id) AS (SELECT id FROM workspace WHERE forked_from_workspace_id IS NULL + UNION SELECT w.id FROM workspace w JOIN reachable r ON w.forked_from_workspace_id = r.id) + SELECT count(*)::int FROM workspace w LEFT JOIN reachable r ON r.id = w.id WHERE r.id IS NULL) AS unreachable + ` + const [bridge] = await sql`SELECT to_regclass('public.project_workspace') IS NOT NULL AS present` + if (bridge.present) { + const [mismatch] = + await sql`SELECT count(*)::int AS count FROM workspace w FULL JOIN project_workspace pw ON pw.workspace_id = w.id + WHERE w.id IS NULL OR w.project_id IS DISTINCT FROM pw.project_id` + counts.bridge = mismatch.count + } + return counts +} diff --git a/packages/db/migrations/0405_project_membership_enforcement.sql b/packages/db/migrations/0405_project_membership_enforcement.sql index 4e771d40dbf..c843c02caff 100644 --- a/packages/db/migrations/0405_project_membership_enforcement.sql +++ b/packages/db/migrations/0405_project_membership_enforcement.sql @@ -1,136 +1,14 @@ --- Each family commits separately; never retain locks across the full backfill. --- The migration runner serializes runners with its session advisory lock. +-- Preparation and assignments are operator-controlled; this migration only validates and enforces. COMMIT; --> statement-breakpoint -SET statement_timeout = '15min'; ---> statement-breakpoint -CREATE TEMP TABLE IF NOT EXISTS project_backfill_roots (id text PRIMARY KEY) ON COMMIT PRESERVE ROWS; -TRUNCATE project_backfill_roots; --- Only legacy families need assignment locks; existing complete Projects only need validation. -WITH RECURSIVE ancestors(id, parent_id) AS ( - SELECT w.id, w.forked_from_workspace_id FROM workspace w - WHERE w.project_id IS NULL - UNION - SELECT w.id, w.forked_from_workspace_id FROM workspace w JOIN ancestors a ON a.parent_id = w.id -) -INSERT INTO project_backfill_roots SELECT id FROM ancestors WHERE parent_id IS NULL; ---> statement-breakpoint -CREATE OR REPLACE PROCEDURE pg_temp.backfill_project_families() LANGUAGE plpgsql AS $$ -DECLARE - root_id text; - previous_root text; - environment_id text; - family_ids text[]; - current_ids text[]; - project_ids text[]; - target_project text; - root workspace%ROWTYPE; - existing project%ROWTYPE; - archive_time timestamp; - attempts integer := 0; - completed integer := 0; - assigned integer := 0; - inserted integer; - retry boolean; -BEGIN - LOOP - SELECT id INTO root_id FROM project_backfill_roots - WHERE previous_root IS NULL OR id > previous_root ORDER BY id LIMIT 1; - EXIT WHEN root_id IS NULL; - -- A pathological family cannot hold environment locks indefinitely (PostgreSQL 17+). - PERFORM set_config('transaction_timeout', '5s', true); - PERFORM set_config('lock_timeout', '1s', true); - retry := false; - BEGIN - WITH RECURSIVE family(id) AS ( - SELECT id FROM workspace WHERE id = root_id AND forked_from_workspace_id IS NULL - UNION - SELECT w.id FROM workspace w JOIN family f ON w.forked_from_workspace_id = f.id - ) SELECT array_agg(id ORDER BY id) INTO family_ids FROM (SELECT id FROM family LIMIT 1001) bounded; - IF cardinality(family_ids) > 1000 THEN - RAISE EXCEPTION 'Project backfill family exceeds 1000 environments' USING ERRCODE = '54000', DETAIL = root_id; - END IF; - IF family_ids IS NOT NULL THEN - -- Application writers take the shared form before reading membership, including absence. - FOREACH environment_id IN ARRAY family_ids LOOP - IF NOT pg_try_advisory_xact_lock(hashtextextended('project-backfill:' || environment_id, 0)) THEN - RAISE EXCEPTION 'Project backfill environment is busy' USING ERRCODE = '55P03'; - END IF; - END LOOP; - -- Do not wait while holding a partial lock set; unrelated environments remain writable. - PERFORM id FROM workspace WHERE id = ANY(family_ids) ORDER BY id FOR NO KEY UPDATE NOWAIT; - WITH RECURSIVE family(id) AS ( - SELECT id FROM workspace WHERE id = root_id AND forked_from_workspace_id IS NULL - UNION - SELECT w.id FROM workspace w JOIN family f ON w.forked_from_workspace_id = f.id - ) SELECT array_agg(id ORDER BY id) INTO current_ids FROM (SELECT id FROM family LIMIT 1001) bounded; - IF family_ids IS DISTINCT FROM current_ids THEN - RAISE EXCEPTION 'Project backfill lineage changed during discovery' USING ERRCODE = '55P03'; - END IF; - SELECT * INTO STRICT root FROM workspace WHERE id = root_id; - IF EXISTS (SELECT 1 FROM workspace WHERE id = ANY(family_ids) AND organization_id IS DISTINCT FROM root.organization_id) THEN - RAISE EXCEPTION 'Project backfill family spans organizations; reconcile before retrying' USING ERRCODE = '55000', DETAIL = root_id; - END IF; - SELECT array_agg(DISTINCT project_id ORDER BY project_id) INTO project_ids - FROM workspace WHERE id = ANY(family_ids) AND project_id IS NOT NULL; - IF cardinality(project_ids) > 1 THEN - RAISE EXCEPTION 'Project backfill family spans Projects; reconcile before retrying' USING ERRCODE = '55000', DETAIL = root_id; - END IF; - SELECT CASE WHEN count(*) = count(archived_at) THEN max(archived_at) END INTO archive_time - FROM workspace WHERE id = ANY(family_ids); - target_project := project_ids[1]; - IF target_project IS NOT NULL THEN - IF NOT pg_try_advisory_xact_lock(hashtextextended('project:' || target_project, 0)) THEN - RAISE EXCEPTION 'Project backfill Project is busy' USING ERRCODE = '55P03'; - END IF; - SELECT * INTO STRICT existing FROM project WHERE id = target_project FOR UPDATE NOWAIT; - IF existing.organization_id IS DISTINCT FROM root.organization_id - OR (existing.archived_at IS NULL) <> (archive_time IS NULL) - OR EXISTS (SELECT 1 FROM workspace WHERE project_id = target_project AND NOT id = ANY(family_ids)) THEN - RAISE EXCEPTION 'Project backfill existing assignment has incompatible scope, archive state, or lineage' USING ERRCODE = '55000', DETAIL = root_id; - END IF; - ELSE - target_project := gen_random_uuid()::text; - INSERT INTO project (id, name, owner_id, organization_id, archived_at) - VALUES (target_project, left(coalesce(nullif(btrim(root.name), ''), 'Untitled'), 90) || ' - Project', root.owner_id, root.organization_id, archive_time); - END IF; - UPDATE workspace SET project_id = target_project WHERE id = ANY(family_ids) AND project_id IS NULL; - GET DIAGNOSTICS inserted = ROW_COUNT; - assigned := assigned + inserted; - END IF; - EXCEPTION WHEN lock_not_available OR deadlock_detected OR serialization_failure THEN - retry := true; - END; - -- Commit outside the exception subtransaction, releasing every row/advisory lock before retry. - COMMIT; - IF retry THEN - attempts := attempts + 1; - IF attempts >= 20 THEN - RAISE EXCEPTION 'Project backfill family remains busy; safe to retry migration' USING ERRCODE = '55P03', DETAIL = root_id; - END IF; - PERFORM pg_sleep(0.05 + random() * 0.15); - COMMIT; - ELSE - attempts := 0; - completed := completed + 1; - previous_root := root_id; - IF completed % 100 = 0 THEN - RAISE NOTICE 'Project backfill: % families processed, % memberships assigned', completed, assigned; - END IF; - END IF; - END LOOP; - RAISE NOTICE 'Project backfill complete: % families processed, % memberships assigned', completed, assigned; -END; -$$; ---> statement-breakpoint -CALL pg_temp.backfill_project_families(); ---> statement-breakpoint -DROP PROCEDURE pg_temp.backfill_project_families(); --- migration-safe: session-local scratch table created above; no application readers or persistent data. -DROP TABLE pg_temp.project_backfill_roots; ---> statement-breakpoint SET statement_timeout = '60s'; --> statement-breakpoint +DO $$ BEGIN + IF NOT pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator', 0)) THEN + RAISE EXCEPTION 'Project preparation is still running; finish it before enforcement' USING ERRCODE = '55000'; + END IF; +END $$; +--> statement-breakpoint CREATE OR REPLACE FUNCTION pg_temp.validate_project_membership() RETURNS void LANGUAGE plpgsql AS $$ BEGIN IF EXISTS ( @@ -143,7 +21,7 @@ BEGIN RAISE EXCEPTION 'Project backfill found a fork cycle or missing parent; reconcile before retrying' USING ERRCODE = '55000'; END IF; IF EXISTS (SELECT 1 FROM workspace w WHERE w.project_id IS NULL) THEN - RAISE EXCEPTION 'Project backfill found newly unassigned or detached environments; retry migration discovery' USING ERRCODE = '55P03'; + RAISE EXCEPTION 'Project preparation is incomplete; run backfill-projects.ts plan/repair/apply/verify before enforcement' USING ERRCODE = '55000'; END IF; IF EXISTS ( SELECT 1 FROM project p LEFT JOIN workspace w ON w.project_id = p.id @@ -165,7 +43,7 @@ BEGIN IF EXISTS ( SELECT 1 FROM project p JOIN workspace w ON w.project_id = p.id JOIN workflow f ON f.workspace_id = w.id - WHERE p.archived_at IS NOT NULL AND f.archived_at IS NULL + WHERE (p.archived_at IS NOT NULL OR w.archived_at IS NOT NULL) AND f.archived_at IS NULL ) THEN RAISE EXCEPTION 'Project enforcement requires archived Projects to have no active workflows' USING ERRCODE = '55000'; END IF; @@ -384,3 +262,6 @@ DROP FUNCTION pg_temp.validate_project_membership(); SET statement_timeout = 0; --> statement-breakpoint SET lock_timeout = '5s'; + +--> statement-breakpoint +SELECT pg_advisory_unlock(hashtextextended('sim:project-backfill-operator', 0)); diff --git a/packages/db/scripts/project-contract-required.ts b/packages/db/scripts/project-contract-required.ts index fdaa662e403..f392b48cd03 100644 --- a/packages/db/scripts/project-contract-required.ts +++ b/packages/db/scripts/project-contract-required.ts @@ -1,3 +1,4 @@ +import { verifyProjectBackfill } from '@sim/db/maintenance/project-backfill' import journal from '@sim/db/migrations/meta/_journal.json' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' @@ -41,6 +42,14 @@ try { throw new Error('Deploy the workspace Project column expansion before enforcing membership') } required = !applied.complete + if (required) { + const counts = await verifyProjectBackfill(sql) + if (Object.values(counts).some((count) => count !== 0)) { + throw new Error( + `Project preparation is incomplete (${JSON.stringify(counts)}); run the reviewed backfill tool before deployment` + ) + } + } } process.stdout.write(`required=${required}\n`) } catch (error) { diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 8afc73bf22b..9e4b1afc986 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -1,8 +1,15 @@ import { execFile } from 'node:child_process' +import { once } from 'node:events' import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { promisify } from 'node:util' +import { + assignProjectBackfillBatch, + discoverProjectBackfill, + projectBackfillDatabaseId, + verifyProjectBackfill, +} from '@sim/db/maintenance/project-backfill' import journal from '@sim/db/migrations/meta/_journal.json' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { createDeferred } from '@sim/testing/helpers/deferred' @@ -65,11 +72,24 @@ async function applyMigration(sql: Sql, source: string) { } throw error } finally { + await reserved + .unsafe("SELECT pg_advisory_unlock(hashtextextended('sim:project-backfill-operator',0))") + .catch(() => undefined) reserved.release() } } -async function enforce(sql: Sql) { +async function prepare(sql: Sql) { + const manifest = await discoverProjectBackfill(sql, 'fixture') + for (const family of manifest.families) await assignProjectBackfillBatch(sql, [family]) + if (manifest.conflicts.length) + throw Object.assign(new Error(manifest.conflicts[0].reason), { + code: manifest.conflicts[0].reason.includes('oversized') ? '54000' : '55000', + }) +} + +async function prepareAndEnforce(sql: Sql) { + await prepare(sql) await applyMigration(sql, migration) } @@ -77,18 +97,242 @@ async function seed(sql: Sql) { await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Production', 'owner')` await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) VALUES ('child', 'Staging', 'owner', 'root')` await sql`INSERT INTO workflow VALUES ('flow', 'root', NULL)` - await enforce(sql) + await prepareAndEnforce(sql) } const constraintFailure = (error: unknown) => getPostgresErrorCode(error) === '23514' describe('Project expand/backfill/contract against PostgreSQL', () => { + it('requires operator preparation before SQL enforcement and leaves missing assignments untouched', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('legacy','Legacy','owner')` + await expect(applyMigration(sql, migration)).rejects.toThrow('preparation is incomplete') + expect(await sql`SELECT id FROM project`).toHaveLength(0) + expect(await sql`SELECT id FROM workspace WHERE project_id IS NULL`).toHaveLength(1) + await prepareAndEnforce(sql) + expect(Object.values(await verifyProjectBackfill(sql)).every((count) => count === 0)).toBe( + true + ) + }) + }) + + it('rolls back an entire singleton batch on failure without losing earlier commits', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id,name,owner_id) SELECT lpad(n::text,3,'0'), 'Env ' || n,'owner' FROM generate_series(1,101) n` + const manifest = await discoverProjectBackfill(sql, 'fixture') + expect(await assignProjectBackfillBatch(sql, manifest.families.slice(0, 50))).toMatchObject({ + assigned: 50, + projectsCreated: 50, + }) + await sql.unsafe(`CREATE FUNCTION reject_fixture() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN + IF NEW.name = 'Env 75 - Project' THEN RAISE EXCEPTION 'fixture failure'; END IF; RETURN NEW; END $$; + CREATE TRIGGER reject_fixture BEFORE INSERT ON project FOR EACH ROW EXECUTE FUNCTION reject_fixture();`) + await expect( + assignProjectBackfillBatch(sql, manifest.families.slice(50, 100)) + ).rejects.toThrow('fixture failure') + expect(await sql`SELECT id FROM project`).toHaveLength(50) + expect(await sql`SELECT id FROM workspace WHERE project_id IS NULL`).toHaveLength(51) + await sql`DROP TRIGGER reject_fixture ON project` + expect(await assignProjectBackfillBatch(sql, manifest.families.slice(50, 100))).toMatchObject( + { assigned: 50 } + ) + expect(await assignProjectBackfillBatch(sql, manifest.families.slice(0, 50))).toMatchObject({ + assigned: 0, + alreadyAssigned: 50, + projectsCreated: 0, + }) + expect(await sql`SELECT id FROM project`).toHaveLength(100) + }) + }) + + it('rejects a stale owner or archive plan without writing and reports archive repairs during discovery', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('root','Root','owner')` + const manifest = await discoverProjectBackfill(sql, 'fixture') + await sql`INSERT INTO "user" VALUES ('next-owner')` + await sql`UPDATE workspace SET owner_id = 'next-owner'` + await expect(assignProjectBackfillBatch(sql, manifest.families)).rejects.toThrow('changed') + expect(await sql`SELECT id FROM project`).toHaveLength(0) + await sql`UPDATE workspace SET owner_id = 'owner', archived_at = now()` + await sql`INSERT INTO workflow VALUES ('flow','root',NULL)` + await expect(assignProjectBackfillBatch(sql, manifest.families)).rejects.toThrow('changed') + const next = await discoverProjectBackfill(sql, 'fixture') + expect(next.repairs).toMatchObject([{ workspaceId: 'root', workflowIds: ['flow'] }]) + await expect(assignProjectBackfillBatch(sql, next.families)).rejects.toThrow('Archive repair') + expect(await sql`SELECT id FROM project`).toHaveLength(0) + }) + }) + + it('runs the real operator CLI read-only, resumes bounded batches and survives a lost checkpoint', async () => { + await database(async (sql, url) => { + const directory = await mkdtemp(join(tmpdir(), 'project-operator-test-')) + const manifestPath = join(directory, 'manifest.json') + const reportPath = join(directory, 'report.json') + const run = (command: string, extra: string[] = []) => + promisify(execFile)( + 'bun', + [ + '--no-env-file', + 'scripts/backfill-projects.ts', + command, + '--manifest', + manifestPath, + ...(command === 'plan' ? [] : ['--report', reportPath]), + ...extra, + ], + { + cwd: new URL('../../../apps/sim/', import.meta.url), + env: { ...process.env, MIGRATION_DATABASE_URL: url, DATABASE_URL: url }, + timeout: 30000, + } + ) + try { + await sql`INSERT INTO workspace (id,name,owner_id) SELECT lpad(n::text,3,'0'),'Env ' || n,'owner' FROM generate_series(1,101) n` + await run('plan') + const reviewed = await readFile(manifestPath, 'utf8') + await expect(run('plan')).rejects.toMatchObject({ code: 1 }) + expect(await readFile(manifestPath, 'utf8')).toBe(reviewed) + expect(await sql`SELECT id FROM project`).toHaveLength(0) + await expect(run('apply')).rejects.toMatchObject({ code: 1 }) + await expect( + run('apply', ['--ack-release-drained', '--max-batches', '1', '--pause-ms', '1']) + ).rejects.toMatchObject({ code: 2 }) + const report = JSON.parse(await readFile(reportPath, 'utf8')) + expect(report).toMatchObject({ + nextIndex: 50, + assigned: 50, + projectsCreated: 50, + status: 'paused', + }) + const first = + await sql`SELECT id,project_id FROM workspace WHERE project_id IS NOT NULL ORDER BY id` + await rm(reportPath) + const child = execFile( + 'bun', + [ + '--no-env-file', + 'scripts/backfill-projects.ts', + 'apply', + '--manifest', + manifestPath, + '--report', + reportPath, + '--ack-release-drained', + '--pause-ms', + '1000', + ], + { + cwd: new URL('../../../apps/sim/', import.meta.url), + env: { ...process.env, MIGRATION_DATABASE_URL: url, DATABASE_URL: url }, + timeout: 15000, + } + ) + const exited = once(child, 'close') + try { + let checkpointed = false + for (let attempt = 0; attempt < 200; attempt++) { + const text = await readFile(reportPath, 'utf8').catch(() => '{}') + if (JSON.parse(text).nextIndex === 50) { + checkpointed = true + break + } + await sleep(25) + } + expect(checkpointed).toBe(true) + await expect(run('apply', ['--ack-release-drained'])).rejects.toMatchObject({ code: 1 }) + child.kill('SIGTERM') + expect((await exited)[0]).toBe(2) + expect(JSON.parse(await readFile(reportPath, 'utf8'))).toMatchObject({ + status: 'paused', + nextIndex: 50, + assigned: 0, + alreadyAssigned: 50, + }) + expect(await sql`SELECT id FROM project`).toHaveLength(50) + } finally { + if (child.exitCode === null) child.kill('SIGKILL') + await exited + } + await rm(reportPath) + await run('apply', ['--ack-release-drained', '--max-batches', '4', '--pause-ms', '1']) + expect( + await sql`SELECT id,project_id FROM workspace WHERE id = ANY(${first.map((row) => row.id)}) ORDER BY id` + ).toEqual(first) + expect(await sql`SELECT id FROM project`).toHaveLength(101) + await run('verify') + const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) + manifest.databaseId = projectBackfillDatabaseId('postgres://localhost/wrong_test') + await writeFile(manifestPath, JSON.stringify(manifest)) + await expect(run('apply', ['--ack-release-drained'])).rejects.toMatchObject({ code: 1 }) + await applyMigration(sql, migration) + } finally { + await rm(directory, { recursive: true, force: true }) + } + }) + }, 60000) + + it('gives workflow creation share locks precedence and bounds a stalled batch without retaining locks', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('root','Root','owner')` + const manifest = await discoverProjectBackfill(sql, 'fixture') + const held = createDeferred() + const release = createDeferred() + const writer = sql.begin(async (tx) => { + await tx`SELECT id FROM workspace WHERE id = 'root' FOR SHARE` + held.resolve() + await release.promise + await tx`INSERT INTO workflow VALUES ('flow','root',NULL)` + }) + await held.promise + try { + await expect(assignProjectBackfillBatch(sql, manifest.families)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55P03' + ) + expect(await sql`SELECT id FROM project`).toHaveLength(0) + } finally { + release.resolve() + await writer + } + await sql.unsafe(`CREATE FUNCTION delay_fixture() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN PERFORM pg_sleep(8); RETURN NEW; END $$; + CREATE TRIGGER delay_fixture BEFORE INSERT ON project FOR EACH ROW EXECUTE FUNCTION delay_fixture();`) + await expect(assignProjectBackfillBatch(sql, manifest.families)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '57014' + ) + expect(await sql`SELECT id FROM project`).toHaveLength(0) + await sql.begin(async (tx) => { + await tx`SET LOCAL lock_timeout = '100ms'` + await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:root',0))` + await tx`UPDATE workspace SET name = 'Recovered' WHERE id = 'root'` + }) + await sql`DROP TRIGGER delay_fixture ON project` + await assignProjectBackfillBatch(sql, manifest.families) + expect(await sql`SELECT name FROM project`).toEqual([{ name: 'Recovered - Project' }]) + }) + }, 10000) + + it('refuses SQL contraction while an operator holds the run lock', async () => { + await database(async (sql) => { + const connection = await sql.reserve() + try { + await connection`SELECT pg_advisory_lock(hashtextextended('sim:project-backfill-operator',0))` + await expect(applyMigration(sql, migration)).rejects.toThrow('preparation is still running') + expect(await sql`SELECT to_regclass('project_workspace')::text AS name`).toEqual([ + { name: 'project_workspace' }, + ]) + } finally { + await connection`SELECT pg_advisory_unlock(hashtextextended('sim:project-backfill-operator',0))` + connection.release() + } + await applyMigration(sql, migration) + }) + }) + it('validates a bulk archive once per final Project row version', async () => { await database(async (sql) => { await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Root', 'owner')` await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) SELECT 'child-' || n, 'Child', 'owner', 'root' FROM generate_series(1, 200) n` - await enforce(sql) + await prepareAndEnforce(sql) await sql.unsafe(` CREATE SEQUENCE project_validation_count; ALTER FUNCTION project_contract_assert_project(text) RENAME TO measured_project_assert; @@ -197,6 +441,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { ]) await sql`DELETE FROM project WHERE id = 'empty'` await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('legacy', 'Legacy', 'owner')` + await prepare(sql) await writeFile( join(directory, '0405_project_membership_enforcement.sql'), `${migration}\n--> statement-breakpoint\nSELECT 1 / 0;\n` @@ -259,7 +504,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await held.promise try { const started = performance.now() - await expect(enforce(sql)).rejects.toSatisfy( + await expect(prepareAndEnforce(sql)).rejects.toSatisfy( (error: unknown) => getPostgresErrorCode(error) === '55P03' ) expect(performance.now() - started).toBeLessThan(2000) @@ -276,7 +521,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { release.resolve() await reader } - await enforce(sql) + await prepareAndEnforce(sql) expect( await sql`SELECT id FROM workspace WHERE id = 'live' AND project_id = 'live-project'` ).toHaveLength(1) @@ -328,7 +573,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await tx`INSERT INTO workspace (id, project_id, name, owner_id) VALUES ('live', 'live-project', 'Live', 'owner')` await tx`SELECT * FROM project` }) - await enforce(sql) + await prepareAndEnforce(sql) expect( await sql`SELECT id FROM workspace WHERE id = 'live' AND project_id = 'live-project'` ).toHaveLength(1) @@ -338,7 +583,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { it('backfills legacy environments before installing mandatory membership', async () => { await database(async (sql) => { await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('legacy', 'Legacy', 'owner')` - await enforce(sql) + await prepareAndEnforce(sql) expect( await sql`SELECT 1 FROM workspace WHERE id = 'legacy' AND project_id IS NOT NULL` ).toHaveLength(1) @@ -351,8 +596,8 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { it('supports a fresh database, repeated installation, and atomic first-environment creation', async () => { await database(async (sql) => { - await enforce(sql) - await enforce(sql) + await prepareAndEnforce(sql) + await prepareAndEnforce(sql) await sql.begin(async (tx) => { await tx`INSERT INTO project (id, name, owner_id) VALUES ('new', 'New', 'owner')` await tx`INSERT INTO workspace (id, project_id, name, owner_id) VALUES ('first', 'new', 'First', 'owner')` @@ -368,7 +613,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { it('rejects invalid archive/scope/fork changes and commits an atomic subtree disconnect', async () => { await database(async (sql) => { await seed(sql) - await enforce(sql) + await prepareAndEnforce(sql) await expect(sql`UPDATE project SET archived_at = now()`).rejects.toSatisfy(constraintFailure) await expect( sql`UPDATE workspace SET organization_id = 'org' WHERE id = 'child'` @@ -397,7 +642,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { it('requires workflows to be archived when archiving their Project and environments', async () => { await database(async (sql) => { await seed(sql) - await enforce(sql) + await prepareAndEnforce(sql) await expect( sql.begin(async (tx) => { await tx`UPDATE project SET archived_at = now()` @@ -452,7 +697,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { async (isolation) => { await database(async (sql) => { await seed(sql) - await enforce(sql) + await prepareAndEnforce(sql) const archived = createDeferred() const release = createDeferred() const first = sql.begin(async (tx) => { @@ -503,11 +748,11 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sql`INSERT INTO workflow VALUES ('flow', 'root', NULL)` await sql`UPDATE workspace SET archived_at = now()` await sql`UPDATE project SET archived_at = now()` - await expect(enforce(sql)).rejects.toSatisfy( + await expect(prepareAndEnforce(sql)).rejects.toSatisfy( (error: unknown) => getPostgresErrorCode(error) === '55000' ) await sql`UPDATE workflow SET archived_at = now()` - await enforce(sql) + await prepareAndEnforce(sql) }) }) it('groups fork families, preserves partial assignments, and retains archived and detached Projects on replay', async () => { @@ -523,7 +768,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { ('g-archived', 'Archive child', 'owner', NULL, 'f-archived', '2025-02-01')` await sql`INSERT INTO project (id, name, owner_id, organization_id) VALUES ('existing', 'Keep name', 'owner', 'org')` await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('existing', 'a-root')` - await enforce(sql) + await prepareAndEnforce(sql) expect(await sql`SELECT id FROM workspace WHERE project_id = 'existing' ORDER BY id`).toEqual( [{ id: 'a-root' }, { id: 'b-child' }, { id: 'c-grandchild' }] ) @@ -536,7 +781,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { { name: 'Detached - Project', owner_id: 'owner', archived_at: null }, { name: 'Keep name', owner_id: 'owner', archived_at: null }, ]) - await enforce(sql) + await prepareAndEnforce(sql) expect( await sql`SELECT id, name, owner_id, archived_at::text FROM project ORDER BY name` ).toEqual(projects) @@ -549,7 +794,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { ('a-good', 'Good', 'owner', NULL, NULL), ('b-root', 'Root', 'owner', 'org', NULL), ('b-child', 'Child', 'owner', NULL, 'b-root')` - await expect(enforce(sql)).rejects.toSatisfy( + await expect(prepareAndEnforce(sql)).rejects.toSatisfy( (error: unknown) => getPostgresErrorCode(error) === '55000' ) const committed = @@ -562,7 +807,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sql`SELECT 1 FROM pg_trigger WHERE tgname = 'project_contract_check'` ).toHaveLength(0) await sql`UPDATE workspace SET organization_id = 'org' WHERE id = 'b-child'` - await enforce(sql) + await prepareAndEnforce(sql) expect( await sql`SELECT project_id FROM workspace WHERE id = 'a-good' AND project_id IS NOT NULL` ).toEqual(committed) @@ -571,98 +816,43 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) - it('releases completed-family locks while a busy family retries and leaves unrelated writes available', async () => { + it('defers a locked family, commits unrelated work and detects a new fork before replay', async () => { await database(async (sql) => { - await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('a-complete', 'First', 'owner'), ('b-busy', 'Second', 'owner')` - const locked = createDeferred() + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('a-free','Free','owner'),('b-busy','Busy','owner')` + const manifest = await discoverProjectBackfill(sql, 'fixture') + const held = createDeferred() const release = createDeferred() const writer = sql.begin(async (tx) => { - await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:b-busy', 0))` - locked.resolve() + await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:b-busy',0))` + held.resolve() await release.promise - await tx`UPDATE workspace SET name = 'Updated before backfill' WHERE id = 'b-busy'` + await tx`INSERT INTO workspace (id,name,owner_id,forked_from_workspace_id) VALUES ('child','Child','owner','b-busy')` }) - await locked.promise - const migrationResult = enforce(sql).then( - () => null, - (error: unknown) => error - ) + await held.promise try { - let committed = false - for (let attempt = 0; attempt < 100; attempt++) { - if ( - (await sql`SELECT 1 FROM workspace WHERE id = 'a-complete' AND project_id IS NOT NULL`) - .length - ) { - committed = true - break - } - await sleep(10) - } - expect(committed).toBe(true) - expect( - await sql`SELECT 1 FROM workspace WHERE id = 'b-busy' AND project_id IS NOT NULL` - ).toHaveLength(0) + await expect(assignProjectBackfillBatch(sql, [manifest.families[1]])).rejects.toThrow( + 'busy' + ) + expect(await assignProjectBackfillBatch(sql, [manifest.families[0]])).toMatchObject({ + assigned: 1, + }) await sql.begin(async (tx) => { - await tx`SET LOCAL statement_timeout = '500ms'` - await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:a-complete', 0))` - await tx`UPDATE workspace SET name = 'Unrelated live edit' WHERE id = 'a-complete'` - await tx`INSERT INTO workflow VALUES ('live-workflow', 'a-complete', NULL)` + await tx`SET LOCAL lock_timeout = '100ms'` + await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:a-free',0))` + await tx`UPDATE workspace SET name = 'Live edit' WHERE id = 'a-free'` }) } finally { release.resolve() await writer - await migrationResult } - expect(await migrationResult).toBeNull() - expect( - ( - await sql`SELECT p.name FROM project p JOIN workspace w ON w.project_id = p.id WHERE w.id = 'b-busy'` - )[0].name - ).toBe('Updated before backfill - Project') - }) - }) - - it('re-discovers a fork added while the family is busy instead of committing partial membership', async () => { - await database(async (sql) => { - await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Root', 'owner')` - const locked = createDeferred() - const release = createDeferred() - const writer = sql.begin(async (tx) => { - await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:root', 0))` - locked.resolve() - await release.promise - await tx`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) VALUES ('new-child', 'Child', 'owner', 'root')` - }) - await locked.promise - const migrationResult = enforce(sql).then( - () => null, - (error: unknown) => error + await expect(assignProjectBackfillBatch(sql, [manifest.families[1]])).rejects.toThrow( + 'gained descendants' ) - try { - let retrying = false - for (let attempt = 0; attempt < 100; attempt++) { - if ( - ( - await sql`SELECT 1 FROM pg_stat_activity WHERE datname = current_database() AND query LIKE '%CALL pg_temp.backfill_project_families%' AND wait_event = 'PgSleep'` - ).length - ) { - retrying = true - break - } - await sleep(10) - } - expect(retrying).toBe(true) - } finally { - release.resolve() - await writer - await migrationResult - } - expect(await migrationResult).toBeNull() + expect(await sql`SELECT id FROM workspace WHERE project_id IS NULL`).toHaveLength(2) + await prepareAndEnforce(sql) expect( - await sql`SELECT DISTINCT project_id FROM workspace WHERE project_id IS NOT NULL` + await sql`SELECT DISTINCT project_id FROM workspace WHERE id IN ('b-busy','child')` ).toHaveLength(1) - expect(await sql`SELECT 1 FROM workspace WHERE project_id IS NOT NULL`).toHaveLength(2) }) }) @@ -703,7 +893,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { } } const before = await sql`SELECT id, project_id FROM workspace ORDER BY id` - await expect(enforce(sql)).rejects.toSatisfy( + await expect(prepareAndEnforce(sql)).rejects.toSatisfy( (error: unknown) => getPostgresErrorCode(error) === (scenario === 'oversized' ? '54000' : '55000') ) @@ -712,56 +902,12 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { } ) - it('resumes after cancellation between committed families without retaining environment locks', async () => { - await database(async (sql) => { - await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('a-first', 'First', 'owner'), ('b-busy', 'Busy', 'owner')` - const locked = createDeferred() - const release = createDeferred() - const writer = sql.begin(async (tx) => { - await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:b-busy', 0))` - locked.resolve() - await release.promise - }) - await locked.promise - const result = enforce(sql).then( - () => null, - (error: unknown) => error - ) - let committed: string | undefined - try { - for (let attempt = 0; attempt < 100; attempt++) { - const [row] = - await sql`SELECT project_id FROM workspace WHERE id = 'a-first' AND project_id IS NOT NULL` - if (row) { - committed = row.project_id - break - } - await sleep(10) - } - expect(committed).toBeTruthy() - await sql`SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND pid <> pg_backend_pid() AND query LIKE '%CALL pg_temp.backfill_project_families%'` - expect(getPostgresErrorCode(await result)).toBe('57014') - } finally { - release.resolve() - await writer - await result - } - await enforce(sql) - expect( - ( - await sql`SELECT project_id FROM workspace WHERE id = 'a-first' AND project_id IS NOT NULL` - )[0].project_id - ).toBe(committed) - expect(await sql`SELECT 1 FROM workspace WHERE project_id IS NOT NULL`).toHaveLength(2) - }) - }) - it('backfills a representative multi-family dataset through independently committed batches', async () => { await database(async (sql) => { await sql`INSERT INTO workspace (id, name, owner_id) SELECT 'root-' || n, 'Environment ' || n, 'owner' FROM generate_series(1, 100) n` await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) SELECT 'child-' || r || '-' || n, 'Child', 'owner', 'root-' || r FROM generate_series(1, 100) r CROSS JOIN generate_series(1, 20) n` - await enforce(sql) + await prepareAndEnforce(sql) expect((await sql`SELECT count(*)::int AS count FROM project`)[0].count).toBe(100) expect( (await sql`SELECT count(*)::int AS count FROM workspace WHERE project_id IS NOT NULL`)[0] @@ -772,61 +918,6 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { ).toHaveLength(0) }) }) - it('rolls back an overlong family while preserving earlier commits and safely resumes', async () => { - await database(async (sql, url) => { - await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('a-fast', 'Fast', 'owner'), ('b-slow', 'Slow', 'owner')` - await sql.unsafe(` - CREATE FUNCTION delay_project_fixture() RETURNS trigger LANGUAGE plpgsql AS $$ - BEGIN - IF NEW.name = 'Slow - Project' THEN PERFORM pg_sleep(10); END IF; - RETURN NEW; - END; $$; - CREATE TRIGGER delay_project_fixture BEFORE INSERT ON project FOR EACH ROW EXECUTE FUNCTION delay_project_fixture(); - `) - const run = promisify(execFile)( - 'bun', - [ - '--no-env-file', - '-e', - ` - import { readFile } from 'node:fs/promises'; - import { getPostgresErrorCode } from '@sim/utils/errors'; - import postgres from 'postgres'; - const sql = postgres(process.env.TEST_DATABASE_URL, { max: 1, onnotice: () => undefined }); - try { - const migration = await readFile('migrations/0405_project_membership_enforcement.sql', 'utf8'); - for (const statement of migration.split('--> statement-breakpoint')) await sql.unsafe(statement); - await sql.end(); - } catch (error) { - process.stderr.write(getPostgresErrorCode(error) ?? 'unknown'); - process.exit(1); - } - `, - ], - { - cwd: new URL('..', import.meta.url), - env: { ...process.env, TEST_DATABASE_URL: url }, - timeout: 12000, - } - ) - await expect(run).rejects.toMatchObject({ - code: 1, - stderr: expect.stringMatching(/^(25P04|CONNECTION_CLOSED)$/), - }) - const committed = - await sql`SELECT project_id FROM workspace WHERE id = 'a-fast' AND project_id IS NOT NULL` - expect(committed).toHaveLength(1) - expect( - await sql`SELECT 1 FROM workspace WHERE id = 'b-slow' AND project_id IS NOT NULL` - ).toHaveLength(0) - await sql`DROP TRIGGER delay_project_fixture ON project` - await enforce(sql) - expect( - await sql`SELECT project_id FROM workspace WHERE id = 'a-fast' AND project_id IS NOT NULL` - ).toEqual(committed) - expect(await sql`SELECT 1 FROM workspace WHERE project_id IS NOT NULL`).toHaveLength(2) - }) - }, 15000) it.each(['separate-roots', 'large-family'] as const)( 'preserves a complete existing Project with %s without putting it through legacy assignment', async (shape) => { @@ -837,7 +928,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) SELECT 'other-' || n, 'Other', 'owner', ${shape === 'large-family' ? 'root' : null} FROM generate_series(1, ${count}) n` await sql`UPDATE workspace SET project_id = 'existing'` - await enforce(sql) + await prepareAndEnforce(sql) expect(await sql`SELECT id, name FROM project`).toEqual([ { id: 'existing', name: 'Keep this Project' }, ]) @@ -849,7 +940,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { (await sql`SELECT count(*)::int AS count FROM workspace WHERE project_id = 'existing'`)[0] .count ).toBe(count + 1) - await enforce(sql) + await prepareAndEnforce(sql) expect(await sql`SELECT id, name FROM project`).toEqual([ { id: 'existing', name: 'Keep this Project' }, ]) From 4a4b85e0793001a3522e869854e139fd425f8db0 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Thu, 8 Oct 2026 23:45:51 -0700 Subject: [PATCH 055/113] fix(projects): support PostgreSQL 16 column migration --- .github/workflows/checks.yml | 9 ++++ .../__integration__/foundation.integration.ts | 47 +++++++++++++++++++ .../0404_workspace_project_column.sql | 10 +++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 7210aa37488..cf5a918d8c9 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -121,6 +121,15 @@ jobs: bun run test --mode integration lib/billing/core/usage-log.integration.ts --outputFile.json=test-results/integration-pg16.json + - name: Verify Project column compatibility on PostgreSQL 16 + if: matrix.provision == 'push' && matrix.shard == 1 + working-directory: packages/db + env: + TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5433/sim_test + run: >- + bun run test --mode integration scripts/workspace-project-column.integration.ts + --outputFile.json=test-results/integration-projects-pg16.json + - name: Upload integration test reports if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 3f3edd3adc1..712824c056a 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -1350,6 +1350,53 @@ describe('Project foundation at the database and application boundary', () => { } ) + check( + 'column reassignment waits for a legacy membership writer before locking workspace rows', + async () => { + const f = await fixture(false) + const held = createDeferred() + const release = createDeferred() + let replacementId = '' + const legacy = db.transaction(async (tx) => { + await lockProject(tx, f.projectId) + replacementId = await createProjectRecord(tx, { + name: 'Legacy disconnect', + ownerId: f.ownerId, + organizationId: null, + }) + await tx.execute( + sql`SELECT workspace_id FROM project_workspace WHERE workspace_id = ${f.ids[1]} FOR UPDATE` + ) + const [backend] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + held.resolve(backend.pid) + await release.promise + await tx.execute( + sql`UPDATE project_workspace SET project_id = ${replacementId} WHERE workspace_id = ${f.ids[1]}` + ) + }) + const blocker = await held.promise + const detach = db + .transaction((tx) => splitForkProject(tx, f.ids[1])) + .then( + () => null, + (error: unknown) => error + ) + try { + await waitUntilBlockedBy(blocker, "lock='project'") + } finally { + release.resolve() + await legacy + } + expect(await detach).toMatchObject({ + message: 'Project membership changed; retry the operation', + }) + expect( + await db.execute(sql`SELECT w.project_id, pw.project_id AS connector_id + FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id WHERE w.id = ${f.ids[1]}`) + ).toEqual([{ project_id: replacementId, connector_id: replacementId }]) + } + ) + check( 'account teardown re-reads Projects created by an unlink while waiting for the old Project', async () => { diff --git a/packages/db/migrations/0404_workspace_project_column.sql b/packages/db/migrations/0404_workspace_project_column.sql index 5d5e5074841..0714836cf4b 100644 --- a/packages/db/migrations/0404_workspace_project_column.sql +++ b/packages/db/migrations/0404_workspace_project_column.sql @@ -1,7 +1,8 @@ COMMIT; --> statement-breakpoint BEGIN; -SET LOCAL transaction_timeout = '5s'; +SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL + THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true); SET LOCAL statement_timeout = '5s'; -- Install both directions together, without queuing behind a live writer. LOCK TABLE workspace IN ACCESS EXCLUSIVE MODE NOWAIT; @@ -74,7 +75,8 @@ BEGIN ORDER BY w.id COLLATE "C" LIMIT 100 ) batch; EXIT WHEN batch_ids IS NULL; - PERFORM set_config('transaction_timeout', '5s', true); + PERFORM set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL + THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true); retry := false; BEGIN PERFORM id FROM workspace WHERE id = ANY(batch_ids) ORDER BY id COLLATE "C" @@ -109,6 +111,10 @@ BEGIN END; $$; --> statement-breakpoint +-- Older servers need a whole-CALL bound; committed batches survive a timeout and replay. +SELECT set_config('statement_timeout', CASE WHEN current_setting('transaction_timeout', true) IS NULL + THEN '5s' ELSE '15min' END, false); +--> statement-breakpoint CALL pg_temp.copy_workspace_projects(); --> statement-breakpoint DROP PROCEDURE pg_temp.copy_workspace_projects(); From 1ce07fa0fb4161b3ea821cdd272b7c1bbaafc421 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 00:01:11 -0700 Subject: [PATCH 056/113] fix(projects): allow multi-batch copies to finish --- .../0404_workspace_project_column.sql | 5 +--- .../workspace-project-column.integration.ts | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/db/migrations/0404_workspace_project_column.sql b/packages/db/migrations/0404_workspace_project_column.sql index 0714836cf4b..563c917e196 100644 --- a/packages/db/migrations/0404_workspace_project_column.sql +++ b/packages/db/migrations/0404_workspace_project_column.sql @@ -47,6 +47,7 @@ CREATE OR REPLACE TRIGGER project_workspace_sync_column FOR EACH ROW EXECUTE FUNCTION project_workspace_sync_column_fn(); COMMIT; --> statement-breakpoint +-- This covers index construction and the whole copy CALL; bounded copy batches commit independently. SET statement_timeout = '15min'; --> statement-breakpoint SET lock_timeout = 0; @@ -111,10 +112,6 @@ BEGIN END; $$; --> statement-breakpoint --- Older servers need a whole-CALL bound; committed batches survive a timeout and replay. -SELECT set_config('statement_timeout', CASE WHEN current_setting('transaction_timeout', true) IS NULL - THEN '5s' ELSE '15min' END, false); ---> statement-breakpoint CALL pg_temp.copy_workspace_projects(); --> statement-breakpoint DROP PROCEDURE pg_temp.copy_workspace_projects(); diff --git a/packages/db/scripts/workspace-project-column.integration.ts b/packages/db/scripts/workspace-project-column.integration.ts index c48ae9fe18f..421229c2afb 100644 --- a/packages/db/scripts/workspace-project-column.integration.ts +++ b/packages/db/scripts/workspace-project-column.integration.ts @@ -227,6 +227,29 @@ describe('workspace Project column compatibility migration', () => { }) }) + it('allows several short copy batches to exceed one transaction timeout in total', async () => { + await fixture(async (sql) => { + await sql`ALTER TABLE workspace ADD COLUMN project_id text` + await sql`INSERT INTO workspace (id) + SELECT 'batch-' || lpad(n::text, 4, '0') FROM generate_series(1, 251) n` + await sql`INSERT INTO project_workspace (project_id, workspace_id) + SELECT 'family', id FROM workspace WHERE id LIKE 'batch-%'` + await sql.unsafe(`CREATE FUNCTION delay_copy_batch() RETURNS trigger LANGUAGE plpgsql AS $$ + BEGIN + IF NEW.id IN ('batch-0001', 'batch-0101', 'batch-0201') THEN PERFORM pg_sleep(2); END IF; + RETURN NEW; + END; + $$`) + await sql`CREATE TRIGGER delay_copy_batch BEFORE UPDATE OF project_id ON workspace + FOR EACH ROW EXECUTE FUNCTION delay_copy_batch()` + await applyMigration(sql, migration) + expect( + await sql`SELECT count(*)::int AS count FROM workspace w + JOIN project_workspace pw ON pw.workspace_id = w.id AND pw.project_id = w.project_id` + ).toEqual([{ count: 254 }]) + }) + }, 20000) + it('commits bounded copy batches, retains completed work on failure and safely resumes', async () => { await fixture(async (sql) => { await sql`ALTER TABLE workspace ADD COLUMN project_id text` From 39f54a0a97e00c7060d7347228893f88d1a2ff8e Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 00:16:55 -0700 Subject: [PATCH 057/113] fix(projects): support PostgreSQL 16 preparation and current fixtures --- .github/workflows/checks.yml | 9 ++++ ...pilot-workspace-invitations.integration.ts | 5 +- apps/sim/lib/oauth/shopify.integration.ts | 5 +- .../copilot-permission-config.integration.ts | 6 ++- .../__integration__/foundation.integration.ts | 47 ------------------- apps/sim/lib/projects/backfill-repair.ts | 3 +- .../copilot-file-versions.integration.ts | 3 +- apps/sim/scripts/backfill-projects.ts | 2 +- packages/db/maintenance/project-backfill.ts | 5 +- .../0405_project_membership_enforcement.sql | 5 +- .../scripts/project-contract.integration.ts | 2 +- 11 files changed, 31 insertions(+), 61 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index cf5a918d8c9..44bd7d856e0 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -130,6 +130,15 @@ jobs: bun run test --mode integration scripts/workspace-project-column.integration.ts --outputFile.json=test-results/integration-projects-pg16.json + - name: Verify Project preparation and enforcement on PostgreSQL 16 + if: matrix.provision == 'push' && matrix.shard == 1 + working-directory: packages/db + env: + TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5433/sim_test + run: >- + bun run test --mode integration scripts/project-contract.integration.ts + --outputFile.json=test-results/integration-project-contract-pg16.json + - name: Upload integration test reports if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 diff --git a/apps/sim/lib/invitations/__integration__/copilot-workspace-invitations.integration.ts b/apps/sim/lib/invitations/__integration__/copilot-workspace-invitations.integration.ts index 86aa13e33db..fc5908b0024 100644 --- a/apps/sim/lib/invitations/__integration__/copilot-workspace-invitations.integration.ts +++ b/apps/sim/lib/invitations/__integration__/copilot-workspace-invitations.integration.ts @@ -11,6 +11,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -86,7 +87,7 @@ describe('chat-delegated workspace invitations', () => { createdAt: now, })) ) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Invitation fixture', ownerId: adminId, @@ -131,7 +132,7 @@ describe('chat-delegated workspace invitations', () => { .delete(invitation) .where(inArray(invitation.email, [newInvitee, pendingInvitee, writerInvitee])) await db.delete(auditLog).where(eq(auditLog.workspaceId, workspaceId)) - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(organization).where(eq(organization.id, organizationId)) await db.delete(user).where(inArray(user.id, [adminId, writerId])) }) diff --git a/apps/sim/lib/oauth/shopify.integration.ts b/apps/sim/lib/oauth/shopify.integration.ts index 0cd2360962b..8afe7b7b845 100644 --- a/apps/sim/lib/oauth/shopify.integration.ts +++ b/apps/sim/lib/oauth/shopify.integration.ts @@ -3,6 +3,7 @@ import { createServer, type Server } from 'node:http' import { dirname } from 'node:path' import { db } from '@sim/db' import { account, credential, pendingCredentialDraft, user, workspace } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { createDeferred, type Deferred } from '@sim/testing/helpers/deferred' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' @@ -72,7 +73,7 @@ beforeAll(async () => { updatedAt: new Date(), })) ) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: workspaceId, name: 'Shopify fixture', ownerId: userIds[0], @@ -212,7 +213,7 @@ afterEach((context) => { afterAll(async () => { globalThis.fetch = originalFetch - await db.delete(workspace).where(eq(workspace.id, workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId)) await db.delete(user).where(inArray(user.id, userIds)) if (provider) { provider.closeAllConnections() diff --git a/apps/sim/lib/permission-groups/__integration__/copilot-permission-config.integration.ts b/apps/sim/lib/permission-groups/__integration__/copilot-permission-config.integration.ts index 6c69b851fc9..ad73f1909f9 100644 --- a/apps/sim/lib/permission-groups/__integration__/copilot-permission-config.integration.ts +++ b/apps/sim/lib/permission-groups/__integration__/copilot-permission-config.integration.ts @@ -11,6 +11,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { envFlagsMock } from '@sim/testing/mocks/env-flags.mock' import { generateId } from '@sim/utils/id' import { eq, inArray } from 'drizzle-orm' @@ -73,7 +74,8 @@ describe('chat-delegated permission configuration', () => { createdAt: now, })) ) - await db.insert(workspace).values( + await insertWorkspaceFixture( + db, [workspaceId, otherWorkspaceId].map((id) => ({ id, name: 'Permission config fixture', @@ -131,7 +133,7 @@ describe('chat-delegated permission configuration', () => { }) afterAll(async () => { - await db.delete(workspace).where(inArray(workspace.id, [workspaceId, otherWorkspaceId])) + await deleteWorkspaceFixture(db, inArray(workspace.id, [workspaceId, otherWorkspaceId])) await db.delete(organization).where(eq(organization.id, organizationId)) await db.delete(user).where(inArray(user.id, [restrictedId, defaultMemberId, outsiderId])) }) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index faf416ea5ca..ea5770f66a5 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -1184,53 +1184,6 @@ describe('Project foundation at the database and application boundary', () => { } ) - check( - 'column reassignment waits for a legacy membership writer before locking workspace rows', - async () => { - const f = await fixture(false) - const held = createDeferred() - const release = createDeferred() - let replacementId = '' - const legacy = db.transaction(async (tx) => { - await lockProject(tx, f.projectId) - replacementId = await createProjectRecord(tx, { - name: 'Legacy disconnect', - ownerId: f.ownerId, - organizationId: null, - }) - await tx.execute( - sql`SELECT workspace_id FROM project_workspace WHERE workspace_id = ${f.ids[1]} FOR UPDATE` - ) - const [backend] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) - held.resolve(backend.pid) - await release.promise - await tx.execute( - sql`UPDATE project_workspace SET project_id = ${replacementId} WHERE workspace_id = ${f.ids[1]}` - ) - }) - const blocker = await held.promise - const detach = db - .transaction((tx) => splitForkProject(tx, f.ids[1])) - .then( - () => null, - (error: unknown) => error - ) - try { - await waitUntilBlockedBy(blocker, "lock='project'") - } finally { - release.resolve() - await legacy - } - expect(await detach).toMatchObject({ - message: 'Project membership changed; retry the operation', - }) - expect( - await db.execute(sql`SELECT w.project_id, pw.project_id AS connector_id - FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id WHERE w.id = ${f.ids[1]}`) - ).toEqual([{ project_id: replacementId, connector_id: replacementId }]) - } - ) - check( 'account teardown re-reads Projects created by an unlink while waiting for the old Project', async () => { diff --git a/apps/sim/lib/projects/backfill-repair.ts b/apps/sim/lib/projects/backfill-repair.ts index 1df1fe7ffb3..814fa4e14bc 100644 --- a/apps/sim/lib/projects/backfill-repair.ts +++ b/apps/sim/lib/projects/backfill-repair.ts @@ -25,7 +25,8 @@ export async function repairArchivedProjectEnvironment( ): Promise { const connection = drizzle({ client, schema }) const effects = await connection.transaction(async (tx) => { - await tx.execute(sql`SET LOCAL transaction_timeout = '5s'`) + await tx.execute(sql`SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL + THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true)`) await tx.execute(sql`SET LOCAL statement_timeout = '3s'`) await tx.execute(sql`SET LOCAL lock_timeout = '250ms'`) if ( diff --git a/apps/sim/lib/workspace-files/__integration__/copilot-file-versions.integration.ts b/apps/sim/lib/workspace-files/__integration__/copilot-file-versions.integration.ts index 3788c33cc3b..117aefab907 100644 --- a/apps/sim/lib/workspace-files/__integration__/copilot-file-versions.integration.ts +++ b/apps/sim/lib/workspace-files/__integration__/copilot-file-versions.integration.ts @@ -5,6 +5,7 @@ import { tmpdir } from 'node:os' import path from 'node:path' import { db } from '@sim/db' import { auditLog, organization, user, workspace, workspaceFileVersion } from '@sim/db/schema' +import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, asc, eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -52,7 +53,7 @@ describe('chat-delegated file version history', () => { afterAll(async () => { for (const ids of fixtures) { await db.delete(auditLog).where(eq(auditLog.workspaceId, ids.workspaceId)) - await db.delete(workspace).where(eq(workspace.id, ids.workspaceId)) + await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) await db.delete(organization).where(eq(organization.id, ids.organizationId)) await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId])) } diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index 314173aab0d..a3cc246215c 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -127,7 +127,7 @@ async function main() { From apps/sim: bun --no-env-file scripts/backfill-projects.ts [options]\n plan Read-only discovery; atomically writes a new --manifest PATH (never overwrites).\nrepair Repair only archived environments listed in --manifest; requires --report PATH.\napply Assign reviewed families from --manifest; requires --report PATH.\nverify Fresh database validation; include --manifest and --report to verify repair completion.\nstatus Read --report; does not connect or resume.\n Writes require MIGRATION_DATABASE_URL pointing directly to the primary and --ack-release-drained.\nDeploy #8830 and verify old servers/workers drained first. Keep the reviewed manifest and report\nin durable private job storage. Review conflicts and repairs before repair/apply. Repair requires\nthe app runtime environment (DATABASE_URL must target the same database) for provider cleanup.\n -Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (1–3600), --pause-ms 100 (1–60000).\nA budget limits scheduling; an in-flight transaction has a separate 5s ceiling. Reuse the same report\nto resume; lost reports can be recreated safely from the same manifest. Changed families need a\nnew plan/report. Complete verify plus deployment evidence is required before #8590 enforcement.\nExit: 0 complete, 2 paused/incomplete, 1 failure. Never run db:migrate to launch this tool.\n`) +Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (1–3600), --pause-ms 100 (1–60000).\nA budget limits scheduling. Transactions use 3s statement limits and a 5s total limit on PG17+,\notherwise a 5s idle-transaction limit on PG16. Reuse the same report\nto resume; lost reports can be recreated safely from the same manifest. Changed families need a\nnew plan/report. Complete verify plus deployment evidence is required before #8590 enforcement.\nExit: 0 complete, 2 paused/incomplete, 1 failure. Never run db:migrate to launch this tool.\n`) return } const [command] = positionals diff --git a/packages/db/maintenance/project-backfill.ts b/packages/db/maintenance/project-backfill.ts index a2655e3a4f5..e952f9a3f68 100644 --- a/packages/db/maintenance/project-backfill.ts +++ b/packages/db/maintenance/project-backfill.ts @@ -62,7 +62,7 @@ export async function assertProjectBackfillDatabase(sql: Sql, write: boolean): P AND table_name = 'workspace' AND column_name = 'project_id') AS expanded ` if (!state?.expanded) throw new Error('Deploy #8830 before Project preparation') - if (state.version < 170000) throw new Error('Project preparation requires PostgreSQL 17+') + if (state.version < 160000) throw new Error('Project preparation requires PostgreSQL 16+') if (write && (state.replica || state.readonly === 'on')) throw new Error('Project writes require the primary database') } @@ -219,7 +219,8 @@ export async function assignProjectBackfillBatch(sql: Sql, families: ProjectBack throw new Error('Expected 1–50 singletons or one complete fork family') for (const family of families) validateFamily(family) return sql.begin(async (tx) => { - await tx`SET LOCAL transaction_timeout = '5s'` + await tx`SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL + THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true)` await tx`SET LOCAL statement_timeout = '3s'` await tx`SET LOCAL lock_timeout = '250ms'` const roots = families.map((f) => f.rootId).sort(compareStrings) diff --git a/packages/db/migrations/0405_project_membership_enforcement.sql b/packages/db/migrations/0405_project_membership_enforcement.sql index c843c02caff..b302d011bef 100644 --- a/packages/db/migrations/0405_project_membership_enforcement.sql +++ b/packages/db/migrations/0405_project_membership_enforcement.sql @@ -211,8 +211,9 @@ $$; --> statement-breakpoint BEGIN; --> statement-breakpoint --- Bound the whole table-lock window, including time between client statements. -SET LOCAL transaction_timeout = '5s'; +-- PG17 caps the transaction; PG16 caps idle time, with each statement bounded below. +SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL + THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true); --> statement-breakpoint SET LOCAL lock_timeout = '1s'; --> statement-breakpoint diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 9e4b1afc986..86b49c02376 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -67,7 +67,7 @@ async function applyMigration(sql: Sql, source: string) { await reserved.unsafe(statement) } } catch (error) { - if (!['25P04', 'CONNECTION_CLOSED'].includes(getPostgresErrorCode(error) ?? '')) { + if (!['25P03', '25P04', 'CONNECTION_CLOSED'].includes(getPostgresErrorCode(error) ?? '')) { await reserved.unsafe('ROLLBACK') } throw error From 8423fecfb05cf652316f124b91a64b627aa263c0 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 00:23:00 -0700 Subject: [PATCH 058/113] fix(projects): reconcile memberships during schema push --- .github/workflows/checks.yml | 2 +- packages/db/scripts/push.integration.ts | 85 ++++++++++++++++--- packages/db/scripts/push.ts | 9 ++ .../scripts/reconcile-project-membership.ts | 49 +++++++++++ 4 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 packages/db/scripts/reconcile-project-membership.ts diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index cf5a918d8c9..df5608412ee 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -127,7 +127,7 @@ jobs: env: TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5433/sim_test run: >- - bun run test --mode integration scripts/workspace-project-column.integration.ts + bun run test --mode integration scripts/workspace-project-column.integration.ts scripts/push.integration.ts --outputFile.json=test-results/integration-projects-pg16.json - name: Upload integration test reports diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index 0017eca669a..7d5ec0923e0 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -84,6 +84,19 @@ ${source}` ) } + function runPush(args: string[]) { + return spawnSync( + 'bun', + ['--no-env-file', fileURLToPath(new URL('./push.ts', import.meta.url)), ...args], + { + cwd: directory, + env: { ...process.env, DATABASE_URL: fixtureUrl, MIGRATION_DATABASE_URL: fixtureUrl }, + encoding: 'utf8', + timeout: 30_000, + } + ) + } + async function legacyColumns() { await sql`CREATE TABLE records (id text PRIMARY KEY, old_label text, old_enabled boolean)` await sql`INSERT INTO records VALUES ('existing', 'original value', true)` @@ -121,18 +134,6 @@ ${source}` export const knowledgeBases = pgTable('knowledge_base', { id: text('id').primaryKey(), isSearchIndex: boolean('is_search_index').notNull().default(false), })`) - function runPush(args: string[]) { - return spawnSync( - 'bun', - ['--no-env-file', fileURLToPath(new URL('./push.ts', import.meta.url)), ...args], - { - cwd: directory, - env: { ...process.env, DATABASE_URL: fixtureUrl, MIGRATION_DATABASE_URL: fixtureUrl }, - encoding: 'utf8', - timeout: 30_000, - } - ) - } const denied = runPush([]) expect(denied.error).toBeUndefined() expect(denied.status, denied.stdout + denied.stderr).toBe(1) @@ -156,6 +157,66 @@ export const knowledgeBases = pgTable('knowledge_base', { ]) }, 30_000) + it('preserves legacy Project assignments and synchronizes both writers through schema push and replay', async () => { + await sql`CREATE TABLE project (id text PRIMARY KEY)` + await sql`CREATE TABLE workspace (id text PRIMARY KEY, forked_from_workspace_id text)` + await sql`CREATE TABLE project_workspace (project_id text NOT NULL, workspace_id text NOT NULL CONSTRAINT project_workspace_workspace_id_unique UNIQUE)` + await sql`INSERT INTO project VALUES ('family'), ('singleton')` + await sql`INSERT INTO workspace VALUES ('root', NULL), ('fork', 'root'), ('standalone', NULL)` + await sql`INSERT INTO project_workspace VALUES ('family', 'root'), ('family', 'fork'), ('singleton', 'standalone')` + await schema(`export const projects = pgTable('project', { id: text('id').primaryKey() }) +export const workspaces = pgTable('workspace', { + id: text('id').primaryKey(), forkedFromWorkspaceId: text('forked_from_workspace_id'), projectId: text('project_id'), +}) +export const memberships = pgTable('project_workspace', { + projectId: text('project_id').notNull(), workspaceId: text('workspace_id').notNull().unique(), +})`) + const first = runPush(['--force']) + expect(first.error, first.stderr).toBeUndefined() + // Other reconcilers require their own tables; their failure must not undo Project preparation. + expect( + await sql`SELECT id, project_id FROM workspace ORDER BY id`, + first.stdout + first.stderr + ).toEqual([ + { id: 'fork', project_id: 'family' }, + { id: 'root', project_id: 'family' }, + { id: 'standalone', project_id: 'singleton' }, + ]) + await sql`UPDATE project_workspace SET project_id = 'singleton' WHERE workspace_id = 'fork'` + await sql`UPDATE workspace SET project_id = 'family' WHERE id = 'standalone'` + const repeated = runPush(['--force']) + expect(repeated.error, repeated.stderr).toBeUndefined() + expect( + await sql`SELECT w.id, w.project_id, pw.project_id AS legacy FROM workspace w + JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` + ).toEqual([ + { id: 'fork', project_id: 'singleton', legacy: 'singleton' }, + { id: 'root', project_id: 'family', legacy: 'family' }, + { id: 'standalone', project_id: 'family', legacy: 'family' }, + ]) + }, 60_000) + + it('refuses a schema downgrade before recreating the retired Project connector', async () => { + await sql`CREATE TABLE project (id text PRIMARY KEY)` + await sql`CREATE TABLE workspace (id text PRIMARY KEY, project_id text NOT NULL)` + await sql`INSERT INTO project VALUES ('retained')` + await sql`INSERT INTO workspace VALUES ('environment', 'retained')` + await schema(`export const projects = pgTable('project', { id: text('id').primaryKey() }) +export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), projectId: text('project_id') }) +export const memberships = pgTable('project_workspace', { + projectId: text('project_id').notNull(), workspaceId: text('workspace_id').notNull().unique(), +})`) + const result = runPush(['--force']) + expect(result.status, result.stdout + result.stderr).toBe(1) + expect(await sql`SELECT to_regclass('public.project_workspace') AS legacy`).toEqual([ + { legacy: null }, + ]) + expect( + await sql`SELECT attnotnull FROM pg_attribute WHERE attrelid = 'workspace'::regclass AND attname = 'project_id'` + ).toEqual([{ attnotnull: true }]) + expect(await sql`SELECT project_id FROM workspace`).toEqual([{ project_id: 'retained' }]) + }, 30_000) + it('retires the legacy size bridge without losing bigint or unbackfilled values', async () => { await sql`CREATE TABLE workspace_files (id text PRIMARY KEY, size integer NOT NULL, size_bytes bigint)` await sql`INSERT INTO workspace_files VALUES ('legacy', 123, NULL), ('large', 2147483647, 5000000000)` diff --git a/packages/db/scripts/push.ts b/packages/db/scripts/push.ts index 02519852b1b..0d359b503a7 100644 --- a/packages/db/scripts/push.ts +++ b/packages/db/scripts/push.ts @@ -5,6 +5,7 @@ import postgres, { type Sql } from 'postgres' const logger = createLogger('DatabasePush') const RECONCILIATION_COMMANDS = [ + ['bun', '--env-file=.env', 'run', './scripts/reconcile-project-membership.ts'], ['bun', '--env-file=.env', 'run', './scripts/reconcile-credential-group-resource-policies.ts'], ['bun', '--env-file=.env', 'run', './scripts/reconcile-oauth-provider.ts'], ['bun', '--env-file=.env', 'run', './script-migrations/0016_backfill_search_vectors.ts'], @@ -105,6 +106,14 @@ export async function runPush(args: string[]): Promise { } if (!help) { + const projectCode = await runCommand([ + 'bun', + '--env-file=.env', + 'run', + './scripts/reconcile-project-membership.ts', + '--prepare', + ]) + if (projectCode !== 0) return projectCode const code = await runCommand([ 'bun', '--env-file=.env', diff --git a/packages/db/scripts/reconcile-project-membership.ts b/packages/db/scripts/reconcile-project-membership.ts new file mode 100644 index 00000000000..ff4ded0abc1 --- /dev/null +++ b/packages/db/scripts/reconcile-project-membership.ts @@ -0,0 +1,49 @@ +import { readFile } from 'node:fs/promises' +import { createLogger } from '@sim/logger' +import { getErrorMessage, getPostgresErrorCode } from '@sim/utils/errors' +import postgres from 'postgres' + +const logger = createLogger('ProjectMembershipPush') +const url = process.env.DATABASE_URL +if (!url) throw new Error('DATABASE_URL is required for Project schema push reconciliation') +const sql = postgres(url, { max: 1, prepare: false, connect_timeout: 10, max_lifetime: null }) +try { + const [state] = await sql< + { workspace: boolean; project: boolean; connector: boolean; required: boolean }[] + >` + SELECT to_regclass('public.workspace') IS NOT NULL AS workspace, + to_regclass('public.project') IS NOT NULL AS project, + to_regclass('public.project_workspace') IS NOT NULL AS connector, + EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = to_regclass('public.workspace') + AND attname = 'project_id' AND NOT attisdropped AND attnotnull) AS required + ` + if (state.workspace && state.required && !state.connector) { + throw new Error( + 'Project membership is already contracted; schema push from the compatibility release is unsupported. Use the current release for schema changes' + ) + } + if (!process.argv.includes('--prepare') && state.workspace && state.project && state.connector) { + // Reuse the migration's bounded copy and bridge so push cannot drift from deployed upgrades. + const source = await readFile( + new URL('../migrations/0404_workspace_project_column.sql', import.meta.url), + 'utf8' + ) + const connection = await sql.reserve() + try { + for (const statement of source.split('--> statement-breakpoint')) { + if (statement.trim()) await connection.unsafe(statement) + } + } finally { + connection.release() + } + logger.info('Project membership copy and synchronization completed') + } +} catch (error) { + logger.error('Project schema push stopped; resolve the error and retry', { + code: getPostgresErrorCode(error), + reason: getErrorMessage(error), + }) + process.exitCode = 1 +} finally { + await sql.end({ timeout: 1 }) +} From 8f06830056bd8289572d066b9f637c8fe5c3c0c0 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 00:33:43 -0700 Subject: [PATCH 059/113] fix(projects): enforce safe schema push provisioning --- packages/db/scripts/push.integration.ts | 104 +++++++++--------- .../scripts/reconcile-project-membership.ts | 12 +- 2 files changed, 61 insertions(+), 55 deletions(-) diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index 7d5ec0923e0..e1c5be4e449 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -58,7 +58,7 @@ describe('patched Drizzle push against PostgreSQL', () => { async function schema(source: string) { await writeFile( join(directory, 'schema.ts'), - `import { pgTable, pgSchema, pgEnum, text, integer, bigint, boolean, check } from ${JSON.stringify(import.meta.resolve('drizzle-orm/pg-core'))} + `import { pgTable, pgSchema, pgEnum, text, integer, bigint, boolean, check, timestamp } from ${JSON.stringify(import.meta.resolve('drizzle-orm/pg-core'))} import { sql } from ${JSON.stringify(import.meta.resolve('drizzle-orm'))} ${source}` ) @@ -157,65 +157,71 @@ export const knowledgeBases = pgTable('knowledge_base', { ]) }, 30_000) - it('preserves legacy Project assignments and synchronizes both writers through schema push and replay', async () => { - await sql`CREATE TABLE project (id text PRIMARY KEY)` - await sql`CREATE TABLE workspace (id text PRIMARY KEY, forked_from_workspace_id text)` - await sql`CREATE TABLE project_workspace (project_id text NOT NULL, workspace_id text NOT NULL CONSTRAINT project_workspace_workspace_id_unique UNIQUE)` - await sql`INSERT INTO project VALUES ('family'), ('singleton')` - await sql`INSERT INTO workspace VALUES ('root', NULL), ('fork', 'root'), ('standalone', NULL)` - await sql`INSERT INTO project_workspace VALUES ('family', 'root'), ('family', 'fork'), ('singleton', 'standalone')` - await schema(`export const projects = pgTable('project', { id: text('id').primaryKey() }) + it.each([false, true])( + 'refuses Project contract through push and preserves legacy assignments (copied=%s)', + async (copied) => { + await sql`CREATE TABLE project (id text PRIMARY KEY)` + await sql`CREATE TABLE workspace (id text PRIMARY KEY)` + await sql`CREATE TABLE project_workspace (project_id text NOT NULL, workspace_id text NOT NULL UNIQUE)` + await sql`INSERT INTO project VALUES ('retained')` + await sql`INSERT INTO workspace VALUES ('environment')` + await sql`INSERT INTO project_workspace VALUES ('retained', 'environment')` + if (copied) { + await sql`ALTER TABLE workspace ADD COLUMN project_id text` + await sql`UPDATE workspace SET project_id = 'retained'` + } + await schema(`export const projects = pgTable('project', { id: text('id').primaryKey() }) +export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), projectId: text('project_id').notNull() })`) + const result = runPush(['--force']) + expect(result.error).toBeUndefined() + expect(result.status, result.stdout + result.stderr).toBe(1) + expect(await sql`SELECT * FROM project_workspace`).toEqual([ + { project_id: 'retained', workspace_id: 'environment' }, + ]) + expect(await sql`SELECT id FROM workspace`).toEqual([{ id: 'environment' }]) + expect( + await sql`SELECT attnotnull FROM pg_attribute WHERE attrelid = 'workspace'::regclass AND attname = 'project_id' AND NOT attisdropped` + ).toEqual(copied ? [{ attnotnull: false }] : []) + }, + 30_000 + ) + + it('installs Project lifecycle enforcement for fresh schema push and preserves it on replay', async () => { + await schema(`export const projects = pgTable('project', { + id: text('id').primaryKey(), name: text('name').notNull(), ownerId: text('owner_id').notNull(), + organizationId: text('organization_id'), archivedAt: timestamp('archived_at'), updatedAt: timestamp('updated_at').notNull().defaultNow(), +}) export const workspaces = pgTable('workspace', { - id: text('id').primaryKey(), forkedFromWorkspaceId: text('forked_from_workspace_id'), projectId: text('project_id'), + id: text('id').primaryKey(), name: text('name').notNull(), ownerId: text('owner_id').notNull(), + projectId: text('project_id').notNull().references(() => projects.id, { onDelete: 'restrict' }), + organizationId: text('organization_id'), archivedAt: timestamp('archived_at'), forkedFromWorkspaceId: text('forked_from_workspace_id'), }) -export const memberships = pgTable('project_workspace', { - projectId: text('project_id').notNull(), workspaceId: text('workspace_id').notNull().unique(), +export const workflows = pgTable('workflow', { + id: text('id').primaryKey(), workspaceId: text('workspace_id'), archivedAt: timestamp('archived_at'), })`) const first = runPush(['--force']) expect(first.error, first.stderr).toBeUndefined() - // Other reconcilers require their own tables; their failure must not undo Project preparation. - expect( - await sql`SELECT id, project_id FROM workspace ORDER BY id`, - first.stdout + first.stderr - ).toEqual([ - { id: 'fork', project_id: 'family' }, - { id: 'root', project_id: 'family' }, - { id: 'standalone', project_id: 'singleton' }, - ]) - await sql`UPDATE project_workspace SET project_id = 'singleton' WHERE workspace_id = 'fork'` - await sql`UPDATE workspace SET project_id = 'family' WHERE id = 'standalone'` + await expect( + sql`INSERT INTO project (id,name,owner_id) VALUES ('empty','Empty','owner')` + ).rejects.toMatchObject({ code: '23514' }) + await sql.begin(async (tx) => { + await tx`INSERT INTO project (id,name,owner_id) VALUES ('family','Family','owner')` + await tx`INSERT INTO workspace (id,name,owner_id,project_id,forked_from_workspace_id) VALUES ('root','Root','owner','family',NULL), ('fork','Fork','owner','family','root')` + }) const repeated = runPush(['--force']) expect(repeated.error, repeated.stderr).toBeUndefined() - expect( - await sql`SELECT w.id, w.project_id, pw.project_id AS legacy FROM workspace w - JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` - ).toEqual([ - { id: 'fork', project_id: 'singleton', legacy: 'singleton' }, - { id: 'root', project_id: 'family', legacy: 'family' }, - { id: 'standalone', project_id: 'family', legacy: 'family' }, + expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'fork', project_id: 'family' }, + { id: 'root', project_id: 'family' }, ]) - }, 60_000) - - it('refuses a schema downgrade before recreating the retired Project connector', async () => { - await sql`CREATE TABLE project (id text PRIMARY KEY)` - await sql`CREATE TABLE workspace (id text PRIMARY KEY, project_id text NOT NULL)` - await sql`INSERT INTO project VALUES ('retained')` - await sql`INSERT INTO workspace VALUES ('environment', 'retained')` - await schema(`export const projects = pgTable('project', { id: text('id').primaryKey() }) -export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), projectId: text('project_id') }) -export const memberships = pgTable('project_workspace', { - projectId: text('project_id').notNull(), workspaceId: text('workspace_id').notNull().unique(), -})`) - const result = runPush(['--force']) - expect(result.status, result.stdout + result.stderr).toBe(1) + await expect(sql`DELETE FROM workspace`).rejects.toMatchObject({ code: '23514' }) + await expect( + sql`UPDATE workspace SET organization_id = 'other' WHERE id = 'fork'` + ).rejects.toMatchObject({ code: '23514' }) expect(await sql`SELECT to_regclass('public.project_workspace') AS legacy`).toEqual([ { legacy: null }, ]) - expect( - await sql`SELECT attnotnull FROM pg_attribute WHERE attrelid = 'workspace'::regclass AND attname = 'project_id'` - ).toEqual([{ attnotnull: true }]) - expect(await sql`SELECT project_id FROM workspace`).toEqual([{ project_id: 'retained' }]) - }, 30_000) + }, 60_000) it('retires the legacy size bridge without losing bigint or unbackfilled values', async () => { await sql`CREATE TABLE workspace_files (id text PRIMARY KEY, size integer NOT NULL, size_bytes bigint)` diff --git a/packages/db/scripts/reconcile-project-membership.ts b/packages/db/scripts/reconcile-project-membership.ts index ff4ded0abc1..c9c01d3bf58 100644 --- a/packages/db/scripts/reconcile-project-membership.ts +++ b/packages/db/scripts/reconcile-project-membership.ts @@ -17,15 +17,15 @@ try { EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = to_regclass('public.workspace') AND attname = 'project_id' AND NOT attisdropped AND attnotnull) AS required ` - if (state.workspace && state.required && !state.connector) { + if (state.workspace && (state.connector || !state.required || !state.project)) { throw new Error( - 'Project membership is already contracted; schema push from the compatibility release is unsupported. Use the current release for schema changes' + 'Existing Project databases must complete the reviewed preparation and contract migration 0405 before schema push. Use the phased migration path for retained data; schema push cannot bypass the writer-drain requirement' ) } - if (!process.argv.includes('--prepare') && state.workspace && state.project && state.connector) { - // Reuse the migration's bounded copy and bridge so push cannot drift from deployed upgrades. + if (!process.argv.includes('--prepare') && state.workspace && state.project) { + // Drizzle cannot express lifecycle triggers; fresh push uses the same enforcement as migrations. const source = await readFile( - new URL('../migrations/0404_workspace_project_column.sql', import.meta.url), + new URL('../migrations/0405_project_membership_enforcement.sql', import.meta.url), 'utf8' ) const connection = await sql.reserve() @@ -36,7 +36,7 @@ try { } finally { connection.release() } - logger.info('Project membership copy and synchronization completed') + logger.info('Project membership validation and lifecycle enforcement completed') } } catch (error) { logger.error('Project schema push stopped; resolve the error and retry', { From c2b95cb8ba98ebd35e406191649f1096a956a703 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 00:40:54 -0700 Subject: [PATCH 060/113] test(projects): provision newly merged workspace fixtures --- .../lib/application/copilot-requests.integration.ts | 6 ++++-- .../copilot-credential-members.integration.ts | 6 ++++-- .../__integration__/copilot-file-versions.integration.ts | 6 +++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/sim/ee/access-requests/lib/application/copilot-requests.integration.ts b/apps/sim/ee/access-requests/lib/application/copilot-requests.integration.ts index fc524b01e0c..5680eba9f4d 100644 --- a/apps/sim/ee/access-requests/lib/application/copilot-requests.integration.ts +++ b/apps/sim/ee/access-requests/lib/application/copilot-requests.integration.ts @@ -12,6 +12,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { envFlagsMock } from '@sim/testing/mocks/env-flags.mock' import { generateId } from '@sim/utils/id' import { and, eq, inArray } from 'drizzle-orm' @@ -92,7 +93,8 @@ describe('chat-delegated workspace access requests', () => { createdAt: now, })) ) - await db.insert(workspace).values( + await insertWorkspaceFixture( + db, [workspaceId, otherWorkspaceId].map((id) => ({ id, name: 'Access request fixture', @@ -138,7 +140,7 @@ describe('chat-delegated workspace access requests', () => { .delete(permissionAccessRequest) .where(eq(permissionAccessRequest.organizationId, organizationId)) await db.delete(auditLog).where(eq(auditLog.workspaceId, workspaceId)) - await db.delete(workspace).where(inArray(workspace.id, [workspaceId, otherWorkspaceId])) + await deleteWorkspaceFixture(db, inArray(workspace.id, [workspaceId, otherWorkspaceId])) await db.delete(organization).where(eq(organization.id, organizationId)) await db.delete(user).where(inArray(user.id, [requesterId, peerId])) }) diff --git a/apps/sim/lib/credentials/__integration__/copilot-credential-members.integration.ts b/apps/sim/lib/credentials/__integration__/copilot-credential-members.integration.ts index 46bf4e3e54b..0514106c86f 100644 --- a/apps/sim/lib/credentials/__integration__/copilot-credential-members.integration.ts +++ b/apps/sim/lib/credentials/__integration__/copilot-credential-members.integration.ts @@ -12,6 +12,7 @@ import { user, workspace, } from '@sim/db/schema' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -95,7 +96,8 @@ describe('chat-delegated credential sharing', () => { createdAt: now, })) ) - await db.insert(workspace).values( + await insertWorkspaceFixture( + db, [workspaceId, otherWorkspaceId].map((id) => ({ id, name: 'Credential sharing fixture', @@ -145,7 +147,7 @@ describe('chat-delegated credential sharing', () => { afterAll(async () => { await db.delete(auditLog).where(eq(auditLog.workspaceId, workspaceId)) - await db.delete(workspace).where(inArray(workspace.id, [workspaceId, otherWorkspaceId])) + await deleteWorkspaceFixture(db, inArray(workspace.id, [workspaceId, otherWorkspaceId])) await db.delete(organization).where(eq(organization.id, organizationId)) await db.delete(user).where(inArray(user.id, userIds)) }) diff --git a/apps/sim/lib/workspace-files/__integration__/copilot-file-versions.integration.ts b/apps/sim/lib/workspace-files/__integration__/copilot-file-versions.integration.ts index 0e7cdd13d78..c28d58f670a 100644 --- a/apps/sim/lib/workspace-files/__integration__/copilot-file-versions.integration.ts +++ b/apps/sim/lib/workspace-files/__integration__/copilot-file-versions.integration.ts @@ -15,7 +15,7 @@ import { workspace, workspaceFileVersion, } from '@sim/db/schema' -import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { envFlagsMock } from '@sim/testing/mocks/env-flags.mock' import { generateId } from '@sim/utils/id' import { and, asc, eq, inArray } from 'drizzle-orm' @@ -68,7 +68,7 @@ describe('chat-delegated file version history', () => { afterAll(async () => { if (extraWorkspaceIds.length > 0) - await db.delete(workspace).where(inArray(workspace.id, extraWorkspaceIds)) + await deleteWorkspaceFixture(db, inArray(workspace.id, extraWorkspaceIds)) for (const ids of fixtures) { await db.delete(auditLog).where(eq(auditLog.workspaceId, ids.workspaceId)) await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) @@ -294,7 +294,7 @@ describe('chat-delegated file version history', () => { const fixture = await seedVersionedFile() const otherWorkspaceId = generateId() extraWorkspaceIds.push(otherWorkspaceId) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: otherWorkspaceId, organizationId: fixture.organizationId, name: 'Second workspace', From 2762a31c0a5aa4bbde234d849f6721f93c811259 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 00:43:14 -0700 Subject: [PATCH 061/113] fix(projects): use the direct connection for push reconciliation --- packages/db/scripts/push.integration.ts | 28 +++++++++++++++++++ .../scripts/reconcile-project-membership.ts | 4 +-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index 7d5ec0923e0..23ce813bfb9 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -157,6 +157,34 @@ export const knowledgeBases = pgTable('knowledge_base', { ]) }, 30_000) + it.each([false, true])( + 'uses the direct migration connection for Project reconciliation (application URL present=%s)', + async (applicationUrlPresent) => { + const unavailable = new URL(fixtureUrl) + unavailable.port = '1' + const result = spawnSync( + 'bun', + [ + '--no-env-file', + fileURLToPath(new URL('./reconcile-project-membership.ts', import.meta.url)), + '--prepare', + ], + { + env: { + ...process.env, + DATABASE_URL: applicationUrlPresent ? unavailable.toString() : undefined, + MIGRATION_DATABASE_URL: fixtureUrl, + }, + encoding: 'utf8', + timeout: 15_000, + } + ) + expect(result.error).toBeUndefined() + expect(result.status, result.stdout + result.stderr).toBe(0) + }, + 30_000 + ) + it('preserves legacy Project assignments and synchronizes both writers through schema push and replay', async () => { await sql`CREATE TABLE project (id text PRIMARY KEY)` await sql`CREATE TABLE workspace (id text PRIMARY KEY, forked_from_workspace_id text)` diff --git a/packages/db/scripts/reconcile-project-membership.ts b/packages/db/scripts/reconcile-project-membership.ts index ff4ded0abc1..f16aebc1ade 100644 --- a/packages/db/scripts/reconcile-project-membership.ts +++ b/packages/db/scripts/reconcile-project-membership.ts @@ -4,8 +4,8 @@ import { getErrorMessage, getPostgresErrorCode } from '@sim/utils/errors' import postgres from 'postgres' const logger = createLogger('ProjectMembershipPush') -const url = process.env.DATABASE_URL -if (!url) throw new Error('DATABASE_URL is required for Project schema push reconciliation') +const url = process.env.MIGRATION_DATABASE_URL || process.env.DATABASE_URL +if (!url) throw new Error('A database URL is required for Project schema push reconciliation') const sql = postgres(url, { max: 1, prepare: false, connect_timeout: 10, max_lifetime: null }) try { const [state] = await sql< From abc8ac60506210c1f51d2acf4cedfafd17ef5c31 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 00:59:54 -0700 Subject: [PATCH 062/113] test(billing): wait for parked transactions before racing writers --- .../stripe-sync-convergence.integration.ts | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/apps/sim/lib/billing/webhooks/stripe-sync-convergence.integration.ts b/apps/sim/lib/billing/webhooks/stripe-sync-convergence.integration.ts index 1a757734d4e..5814ecf31e9 100644 --- a/apps/sim/lib/billing/webhooks/stripe-sync-convergence.integration.ts +++ b/apps/sim/lib/billing/webhooks/stripe-sync-convergence.integration.ts @@ -15,6 +15,7 @@ import { type InMemoryStripe, stripeClientMock, } from '@sim/testing/mocks/stripe.mock' +import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' import { type BetterAuthOptions, betterAuth } from 'better-auth' import { createAuthMiddleware } from 'better-auth/api' @@ -372,7 +373,7 @@ type TestTransaction = Parameters[0] * Starts a transaction that takes its locks in `holdLocks`, then parks until released and runs * `finish`. `untilBlocking` resolves once another backend is waiting on one of its locks. */ -function startParkedTransaction( +async function startParkedTransaction( holdLocks: (tx: TestTransaction) => Promise, finish: (tx: TestTransaction) => Promise = async () => {} ) { @@ -393,15 +394,17 @@ function startParkedTransaction( }) async function untilBlocking() { const pid = await holderPid - for (let attempt = 0; attempt < 200; attempt++) { + const deadline = Date.now() + 5000 + while (Date.now() < deadline) { const [row] = await connection<{ blocked: number }[]>` select count(*)::int as blocked from pg_stat_activity where ${pid}::int = any(pg_blocking_pids(pid))` if (row.blocked > 0) return - await new Promise((resolve) => setImmediate(resolve)) + await sleep(10) } throw new Error('No transaction ever waited on the parked one') } + await Promise.race([holderPid, done]) return { done, release, untilBlocking } } @@ -1019,7 +1022,7 @@ describe('Team activation', () => { }) stripe.addSubscription({ id: stripeSubscriptionId, customer: `cus_${subscriptionId}` }) - const cancelling = startParkedTransaction(async (tx) => { + const cancelling = await startParkedTransaction(async (tx) => { await tx .update(subscription) .set({ cancelAtPeriodEnd: true }) @@ -1039,8 +1042,12 @@ describe('Team activation', () => { workspaceIdsToAttach: [], }) ) - await cancelling.untilBlocking() - cancelling.release() + try { + await cancelling.untilBlocking() + } finally { + cancelling.release() + await Promise.allSettled([cancelling.done, activating]) + } await cancelling.done await expect(activating).resolves.toMatchObject({ success: true }) expect((await storedSubscription(subscriptionId)).cancelAtPeriodEnd).toBe(false) @@ -1066,7 +1073,7 @@ describe('operator retry', () => { ) await deadLetter(pauseSync) - const writing = startParkedTransaction( + const writing = await startParkedTransaction( async (tx) => { await tx .update(subscription) @@ -1083,8 +1090,12 @@ describe('operator retry', () => { } ) const requeuing = requeueFromAdminApi(pauseSync) - await writing.untilBlocking() - writing.release() + try { + await writing.untilBlocking() + } finally { + writing.release() + await Promise.allSettled([writing.done, requeuing]) + } await expect(Promise.all([writing.done, requeuing])).resolves.toBeDefined() await deliverUnrelatedUpdate(pro.stripeSubscriptionId) @@ -1107,7 +1118,7 @@ describe('operator retry', () => { ) await deadLetter(cancelSync) - const writing = startParkedTransaction( + const writing = await startParkedTransaction( async (tx) => { await tx .update(subscription) @@ -1129,8 +1140,12 @@ describe('operator retry', () => { timing: 'period_end', actor, }) - await writing.untilBlocking() - writing.release() + try { + await writing.untilBlocking() + } finally { + writing.release() + await Promise.allSettled([writing.done, retrying]) + } await expect(Promise.all([writing.done, retrying])).resolves.toBeDefined() expect((await storedSubscription(org.subscriptionId)).cancelAtPeriodEnd).toBe(true) From e73ed2d77182a52b030ed792a6da1ad90c229b81 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 01:13:44 -0700 Subject: [PATCH 063/113] test(billing): freeze the clock for exact deadline assertions --- apps/sim/lib/billing/core/usage-log.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/sim/lib/billing/core/usage-log.test.ts b/apps/sim/lib/billing/core/usage-log.test.ts index 3c87c10b9fa..8982ae34911 100644 --- a/apps/sim/lib/billing/core/usage-log.test.ts +++ b/apps/sim/lib/billing/core/usage-log.test.ts @@ -5,7 +5,7 @@ import { billingSubscriptionUtilsMock, billingSubscriptionUtilsMockFns, } from '@sim/testing/mocks/billing-subscription-utils.mock' -import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' const { mockInsert, @@ -455,9 +455,14 @@ describe('ledger aggregates', () => { ] beforeEach(() => { + vi.useFakeTimers({ toFake: ['Date'] }) installSharedDbMocks() }) + afterEach(() => { + vi.useRealTimers() + }) + for (const aggregate of aggregates) { it(`${aggregate.name} reads through the bounded ledger transaction`, async () => { const execute = vi.fn().mockResolvedValue([]) From b653f5ea53bba15b9552f4d2a812ab73be6429d7 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 01:23:23 -0700 Subject: [PATCH 064/113] fix(projects): close operator verification and contention gaps --- .../backfill-repair.integration.ts | 25 ++++++- apps/sim/lib/workspaces/lifecycle.ts | 42 ++++++----- apps/sim/scripts/backfill-projects.ts | 32 ++++----- packages/db/maintenance/project-backfill.ts | 6 +- .../0405_project_membership_enforcement.sql | 8 ++- .../scripts/project-contract.integration.ts | 70 +++++++++++++++++-- 6 files changed, 135 insertions(+), 48 deletions(-) diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts index d1380f6b029..c27b462042b 100644 --- a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -4,8 +4,9 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { promisify } from 'node:util' import journal from '@sim/db/migrations/meta/_journal.json' -import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { readTestDatabaseUrl, readTestRedisUrl } from '@sim/db/testing/test-infrastructure' import { generateId } from '@sim/utils/id' +import Redis from 'ioredis' import postgres from 'postgres' import { afterAll, beforeAll, describe, expect, it } from 'vitest' @@ -17,10 +18,19 @@ const client = postgres(url.toString(), { max: 1, onnotice: () => {} }) const directory = await mkdtemp(join(tmpdir(), 'project-repair-test-')) const manifest = join(directory, 'manifest.json') const report = join(directory, 'report.json') +const redisUrl = readTestRedisUrl() +if (!redisUrl) throw new Error('Archive repair integration requires TEST_REDIS_URL') +const cache = new Redis(redisUrl) +const subscriber = new Redis(redisUrl) +const mcpServerId = generateId() +const cacheKey = `mcp:tools:workspace:env:server:${mcpServerId}` +const published: unknown[] = [] +subscriber.on('message', (_channel, message) => published.push(JSON.parse(message))) const environment = { ...process.env, DATABASE_URL: url.toString(), MIGRATION_DATABASE_URL: url.toString(), + REDIS_URL: redisUrl, } const execute = promisify(execFile) const run = (command: string) => @@ -42,6 +52,7 @@ const run = (command: string) => ) beforeAll(async () => { + await subscriber.subscribe('mcp:workflow_tools_changed') await admin.unsafe(`CREATE DATABASE "${name}"`) await client.unsafe( 'CREATE EXTENSION vector; CREATE EXTENSION btree_gin; CREATE EXTENSION pg_trgm' @@ -79,6 +90,8 @@ beforeAll(async () => { }, 120000) afterAll(async () => { + await cache.del(cacheKey) + await Promise.all([cache.quit(), subscriber.quit()]) await client.end({ timeout: 2 }) await admin.unsafe(`DROP DATABASE IF EXISTS "${name}" WITH (FORCE)`) await admin.end() @@ -95,10 +108,16 @@ describe('Operator archive repair against the full compatible schema', () => { await client`INSERT INTO webhook (id,workflow_id,provider,path,provider_config) VALUES ('hook','flow','gitlab','fixture-hook','{}')` await client`INSERT INTO chat (id,workflow_id,user_id,identifier,title) VALUES ('chat','flow','owner','repair-chat','Chat')` await client`INSERT INTO workflow_deployment_version (id,workflow_id,version,state,is_active) VALUES ('deployment','flow',1,'{}',true)` - await client`INSERT INTO workflow_mcp_server (id,workspace_id,created_by,name,is_public) VALUES ('server','env','owner','Server',true)` - await client`INSERT INTO workflow_mcp_tool (id,server_id,workflow_id,tool_name) VALUES ('tool','server','flow','fixture')` + await client`INSERT INTO workflow_mcp_server (id,workspace_id,created_by,name,is_public) VALUES (${mcpServerId},'env','owner','Server',true)` + await client`INSERT INTO workflow_mcp_tool (id,server_id,workflow_id,tool_name) VALUES ('tool',${mcpServerId},'flow','fixture')` + await client`INSERT INTO mcp_servers (id,workspace_id,created_by,name,transport) VALUES (${mcpServerId},'env','owner','Cached server','streamable-http')` + await cache.set(cacheKey, JSON.stringify({ tools: [], expiry: Date.now() + 60000 }), 'EX', 60) await run('plan') await expect(run('repair')).rejects.toMatchObject({ code: 1 }) + expect(await cache.get(cacheKey)).toBeNull() + await expect + .poll(() => published, { timeout: 2000 }) + .toContainEqual({ serverId: mcpServerId, workspaceId: 'env' }) expect(JSON.parse(await readFile(report, 'utf8'))).toMatchObject({ status: 'failed', repairsCompleted: [], diff --git a/apps/sim/lib/workspaces/lifecycle.ts b/apps/sim/lib/workspaces/lifecycle.ts index d58b18c9296..7dad65230db 100644 --- a/apps/sim/lib/workspaces/lifecycle.ts +++ b/apps/sim/lib/workspaces/lifecycle.ts @@ -144,27 +144,31 @@ export async function finishEnvironmentArchive( options: { strictExternalCleanup?: boolean } = {} ): Promise { const { workspaceId } = effects - await mapWithConcurrency(effects.workflows, ARCHIVE_NOTIFICATION_CONCURRENCY, (row) => - finishWorkflowArchive(row.id, workspaceId, row.serverIds, { - requestId, - strictExternalCleanup: options.strictExternalCleanup, - }).catch((error) => - options.strictExternalCleanup - ? Promise.reject(error) - : logger.warn(`[${requestId}] Post-archive notification failed for workflow ${row.id}`, { + try { + await mapWithConcurrency(effects.workflows, ARCHIVE_NOTIFICATION_CONCURRENCY, (row) => + finishWorkflowArchive(row.id, workspaceId, row.serverIds, { + requestId, + strictExternalCleanup: options.strictExternalCleanup, + }).catch((error) => + options.strictExternalCleanup + ? Promise.reject(error) + : logger.warn(`[${requestId}] Post-archive notification failed for workflow ${row.id}`, { + error, + }) + ) + ) + } finally { + await mcpService.clearCache(workspaceId).catch(() => undefined) + if (mcpPubSub) { + for (const serverId of effects.serverIds) { + try { + mcpPubSub.publishWorkflowToolsChanged({ serverId, workspaceId }) + } catch (error) { + logger.warn(`[${requestId}] MCP tools-changed publish failed for server ${serverId}`, { error, }) - ) - ) - await mcpService.clearCache(workspaceId).catch(() => undefined) - if (!mcpPubSub) return - for (const serverId of effects.serverIds) { - try { - mcpPubSub.publishWorkflowToolsChanged({ serverId, workspaceId }) - } catch (error) { - logger.warn(`[${requestId}] MCP tools-changed publish failed for server ${serverId}`, { - error, - }) + } + } } } } diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index a3cc246215c..344925ddd0b 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -125,7 +125,7 @@ async function main() { if (values.help || !positionals.length) { process.stdout.write(`Operator-controlled Project preparation; never runs automatically on deployment.\n From apps/sim: bun --no-env-file scripts/backfill-projects.ts [options]\n -plan Read-only discovery; atomically writes a new --manifest PATH (never overwrites).\nrepair Repair only archived environments listed in --manifest; requires --report PATH.\napply Assign reviewed families from --manifest; requires --report PATH.\nverify Fresh database validation; include --manifest and --report to verify repair completion.\nstatus Read --report; does not connect or resume.\n +plan Read-only discovery; atomically writes a new --manifest PATH (never overwrites).\nrepair Repair only archived environments listed in --manifest; requires --report PATH.\napply Assign reviewed families from --manifest; requires --report PATH.\nverify Validate the database and repair completion; requires --manifest and --report.\nstatus Read --report; does not connect or resume.\n Writes require MIGRATION_DATABASE_URL pointing directly to the primary and --ack-release-drained.\nDeploy #8830 and verify old servers/workers drained first. Keep the reviewed manifest and report\nin durable private job storage. Review conflicts and repairs before repair/apply. Repair requires\nthe app runtime environment (DATABASE_URL must target the same database) for provider cleanup.\n Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (1–3600), --pause-ms 100 (1–60000).\nA budget limits scheduling. Transactions use 3s statement limits and a 5s total limit on PG17+,\notherwise a 5s idle-transaction limit on PG16. Reuse the same report\nto resume; lost reports can be recreated safely from the same manifest. Changed families need a\nnew plan/report. Complete verify plus deployment evidence is required before #8590 enforcement.\nExit: 0 complete, 2 paused/incomplete, 1 failure. Never run db:migrate to launch this tool.\n`) return @@ -207,23 +207,21 @@ Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 return } if (command === 'verify') { + if (!manifestPath || !reportPath) + throw new Error('Verification requires both --manifest and --report') const counts = await verifyProjectBackfill(sql) - if (manifestPath || reportPath) { - if (!manifestPath || !reportPath) - throw new Error('Repair verification needs both --manifest and --report') - const manifest = manifestSchema.parse(await readJson(manifestPath)) - const report = progressSchema.parse(await readJson(reportPath)) - const hash = createHash('sha256').update(JSON.stringify(manifest)).digest('hex') - if ( - manifest.databaseId !== databaseId || - report.databaseId !== databaseId || - report.manifestHash !== hash - ) - throw new Error('Verification artifacts do not match this database and manifest') - counts.pendingCleanup = manifest.repairs.filter( - (repair) => !report.repairsCompleted.includes(repair.workspaceId) - ).length - } + const manifest = manifestSchema.parse(await readJson(manifestPath)) + const report = progressSchema.parse(await readJson(reportPath)) + const hash = createHash('sha256').update(JSON.stringify(manifest)).digest('hex') + if ( + manifest.databaseId !== databaseId || + report.databaseId !== databaseId || + report.manifestHash !== hash + ) + throw new Error('Verification artifacts do not match this database and manifest') + counts.pendingCleanup = manifest.repairs.filter( + (repair) => !report.repairsCompleted.includes(repair.workspaceId) + ).length logger.info('Project verification', { databaseId, ...counts }) if (Object.values(counts).some((count) => count !== 0)) process.exitCode = 2 return diff --git a/packages/db/maintenance/project-backfill.ts b/packages/db/maintenance/project-backfill.ts index e952f9a3f68..a70971edc36 100644 --- a/packages/db/maintenance/project-backfill.ts +++ b/packages/db/maintenance/project-backfill.ts @@ -124,7 +124,11 @@ export async function discoverProjectBackfill( else groups.set(root, [row]) } for (const [rootId, members] of groups) { - if (members.every((row) => row.projectId !== null)) continue + if ( + members.every((row) => row.projectId !== null) && + new Set(members.map((row) => row.projectId)).size === 1 + ) + continue const family = { rootId, members } try { validateFamily(family) diff --git a/packages/db/migrations/0405_project_membership_enforcement.sql b/packages/db/migrations/0405_project_membership_enforcement.sql index b302d011bef..d1ba7301305 100644 --- a/packages/db/migrations/0405_project_membership_enforcement.sql +++ b/packages/db/migrations/0405_project_membership_enforcement.sql @@ -149,7 +149,10 @@ DECLARE target_id text; BEGIN FOR target_id IN SELECT DISTINCT id FROM unnest(target_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP - PERFORM pg_advisory_xact_lock(hashtextextended('project:' || target_id, 0)); + -- BEFORE row triggers already hold row locks; waiting here could invert the application lock order. + IF NOT pg_try_advisory_xact_lock(hashtextextended('project:' || target_id, 0)) THEN + RAISE EXCEPTION 'Project is changing; retry the operation' USING ERRCODE = '55P03'; + END IF; END LOOP; END; $$; @@ -163,12 +166,11 @@ BEGIN IF TG_TABLE_NAME = 'project' THEN IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; - PERFORM pg_advisory_xact_lock(hashtextextended('project:' || coalesce(next_id, previous_id), 0)); ELSE IF TG_OP <> 'INSERT' THEN previous_id := OLD.project_id; END IF; IF TG_OP <> 'DELETE' THEN next_id := NEW.project_id; END IF; - PERFORM project_contract_lock_projects(ARRAY[previous_id, next_id]); END IF; + PERFORM project_contract_lock_projects(ARRAY[previous_id, next_id]); IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; RETURN NEW; END; diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 86b49c02376..165c994a54d 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -260,6 +260,19 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { ).toEqual(first) expect(await sql`SELECT id FROM project`).toHaveLength(101) await run('verify') + for (const artifacts of [[], ['--manifest', manifestPath], ['--report', reportPath]]) { + await expect( + promisify(execFile)( + 'bun', + ['--no-env-file', 'scripts/backfill-projects.ts', 'verify', ...artifacts], + { + cwd: new URL('../../../apps/sim/', import.meta.url), + env: { ...process.env, MIGRATION_DATABASE_URL: url, DATABASE_URL: url }, + timeout: 30000, + } + ) + ).rejects.toMatchObject({ code: 1 }) + } const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) manifest.databaseId = projectBackfillDatabaseId('postgres://localhost/wrong_test') await writeFile(manifestPath, JSON.stringify(manifest)) @@ -692,6 +705,44 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) + it.each([ + ['Project update', "UPDATE project SET name = 'Contending update'"], + ['Project delete', 'DELETE FROM project'], + ['environment update', "UPDATE workspace SET archived_at = now() WHERE id = 'child'"], + ['environment delete', "DELETE FROM workspace WHERE id = 'child'"], + ])('rejects a direct %s promptly when its Project is changing', async (_operation, statement) => { + await database(async (sql) => { + await seed(sql) + const held = createDeferred() + const release = createDeferred() + const writer = sql.begin(async (tx) => { + await tx`UPDATE workflow SET archived_at = now() WHERE workspace_id = 'root'` + await tx`UPDATE workspace SET archived_at = now() WHERE id = 'root'` + held.resolve() + await release.promise + }) + await held.promise + try { + await expect( + sql.begin(async (tx) => { + await tx`SET LOCAL statement_timeout = '500ms'` + await tx.unsafe(statement) + }) + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '55P03') + } finally { + release.resolve() + await writer + } + expect( + await sql`SELECT 1 FROM workspace WHERE id = 'child' AND archived_at IS NULL` + ).toHaveLength(1) + expect(await sql`SELECT 1 FROM project WHERE name = 'Contending update'`).toHaveLength(0) + expect(Object.values(await verifyProjectBackfill(sql)).every((count) => count === 0)).toBe( + true + ) + }) + }) + it.each(['read committed', 'repeatable read'] as const)( 'prevents concurrent last-environment removal under %s', async (isolation) => { @@ -712,6 +763,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { const [connection] = await tx`SELECT pg_backend_pid() AS pid, count(*) FROM workspace WHERE archived_at IS NULL` started.resolve(connection.pid) + await tx`SELECT pg_advisory_xact_lock(hashtextextended('project:' || project_id, 0)) FROM workspace WHERE id = 'child'` await tx`UPDATE workspace SET archived_at = now() WHERE id = 'child'` }) .then( @@ -736,7 +788,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await first } const failure = await second - expect(['23514', '40001', '40P01']).toContain(getPostgresErrorCode(failure)) + expect(['23514', '40001']).toContain(getPostgresErrorCode(failure)) expect(await sql`SELECT 1 FROM workspace WHERE archived_at IS NULL`).toHaveLength(1) }) } @@ -745,14 +797,17 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { it('refuses enforcement when an archived Project retains an active workflow', async () => { await database(async (sql) => { await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Root', 'owner')` + await sql`INSERT INTO project (id, name, owner_id, archived_at) VALUES ('existing', 'Existing', 'owner', now())` + await sql`UPDATE workspace SET project_id = 'existing', archived_at = now()` await sql`INSERT INTO workflow VALUES ('flow', 'root', NULL)` - await sql`UPDATE workspace SET archived_at = now()` - await sql`UPDATE project SET archived_at = now()` - await expect(prepareAndEnforce(sql)).rejects.toSatisfy( + await expect(applyMigration(sql, migration)).rejects.toSatisfy( (error: unknown) => getPostgresErrorCode(error) === '55000' ) + expect(await sql`SELECT to_regclass('project_workspace')::text AS name`).toEqual([ + { name: 'project_workspace' }, + ]) await sql`UPDATE workflow SET archived_at = now()` - await prepareAndEnforce(sql) + await applyMigration(sql, migration) }) }) it('groups fork families, preserves partial assignments, and retains archived and detached Projects on replay', async () => { @@ -893,6 +948,11 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { } } const before = await sql`SELECT id, project_id FROM workspace ORDER BY id` + if (scenario === 'split') { + expect(await discoverProjectBackfill(sql, 'fixture')).toMatchObject({ + conflicts: [{ id: 'root', reason: 'Fork family spans Projects' }], + }) + } await expect(prepareAndEnforce(sql)).rejects.toSatisfy( (error: unknown) => getPostgresErrorCode(error) === (scenario === 'oversized' ? '54000' : '55000') From 38d192dc6a457f8528b2c9848d17fc59285f9212 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 01:29:28 -0700 Subject: [PATCH 065/113] test(billing): stabilize member ledger timeout checks --- apps/sim/lib/billing/organizations/member-limits.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/sim/lib/billing/organizations/member-limits.test.ts b/apps/sim/lib/billing/organizations/member-limits.test.ts index c7749b67de0..a1677e98ff2 100644 --- a/apps/sim/lib/billing/organizations/member-limits.test.ts +++ b/apps/sim/lib/billing/organizations/member-limits.test.ts @@ -6,7 +6,7 @@ import { queueTableRows, resetDbChainMock, } from '@sim/testing/mocks/database.mock' -import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { USAGE_LEDGER_STATEMENT_TIMEOUT_MS } from '@/lib/billing/constants' vi.mock('@/lib/billing/core/billing', () => billingCoreMock) @@ -21,9 +21,14 @@ const mockGetOrganizationSubscription = billingCoreMockFns.mockGetOrganizationSu const { eq: mockEq, gte: mockGte, isNull: mockIsNull, lt: mockLt, or: mockOr } = drizzleOrmMock beforeEach(() => { + vi.useFakeTimers({ toFake: ['Date'] }) resetDbChainMock() }) +afterEach(() => { + vi.useRealTimers() +}) + afterAll(() => { resetDbChainMock() }) From 8c3c334e972ba5701a1bed849fbbf3be0315d4d2 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 01:59:00 -0700 Subject: [PATCH 066/113] fix(projects): await cleanup and bound backfill artifacts --- .../backfill-repair.integration.ts | 45 +++++++++++++++++-- apps/sim/lib/workspaces/lifecycle.ts | 20 +++++---- apps/sim/scripts/backfill-projects.ts | 8 +++- .../scripts/project-contract.integration.ts | 35 ++++++++++++++- 4 files changed, 93 insertions(+), 15 deletions(-) diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts index c27b462042b..139bcc53d65 100644 --- a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -1,10 +1,12 @@ import { execFile } from 'node:child_process' import { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { createServer } from 'node:http' import { tmpdir } from 'node:os' import { join } from 'node:path' import { promisify } from 'node:util' import journal from '@sim/db/migrations/meta/_journal.json' import { readTestDatabaseUrl, readTestRedisUrl } from '@sim/db/testing/test-infrastructure' +import { createDeferred } from '@sim/testing/helpers/deferred' import { generateId } from '@sim/utils/id' import Redis from 'ioredis' import postgres from 'postgres' @@ -26,11 +28,22 @@ const mcpServerId = generateId() const cacheKey = `mcp:tools:workspace:env:server:${mcpServerId}` const published: unknown[] = [] subscriber.on('message', (_channel, message) => published.push(JSON.parse(message))) +const releaseNotifications = createDeferred() +let tailNotified = false +const realtime = createServer(async (request, response) => { + const chunks: Buffer[] = [] + for await (const chunk of request) chunks.push(Buffer.from(chunk)) + const { workflowId } = JSON.parse(Buffer.concat(chunks).toString()) as { workflowId: string } + if (workflowId === 'tail') tailNotified = true + if (workflowId.startsWith('slow-')) await releaseNotifications.promise + response.writeHead(200, { 'content-type': 'application/json' }).end('{}') +}) const environment = { ...process.env, DATABASE_URL: url.toString(), MIGRATION_DATABASE_URL: url.toString(), REDIS_URL: redisUrl, + SOCKET_SERVER_URL: process.env.SOCKET_SERVER_URL, } const execute = promisify(execFile) const run = (command: string) => @@ -52,6 +65,10 @@ const run = (command: string) => ) beforeAll(async () => { + await new Promise((resolve) => realtime.listen(0, '127.0.0.1', resolve)) + const address = realtime.address() + if (!address || typeof address === 'string') throw new Error('Realtime fixture failed to bind') + environment.SOCKET_SERVER_URL = `http://127.0.0.1:${address.port}` await subscriber.subscribe('mcp:workflow_tools_changed') await admin.unsafe(`CREATE DATABASE "${name}"`) await client.unsafe( @@ -90,6 +107,11 @@ beforeAll(async () => { }, 120000) afterAll(async () => { + releaseNotifications.resolve() + await new Promise((resolve, reject) => { + realtime.close((error) => (error ? reject(error) : resolve())) + realtime.closeAllConnections() + }) await cache.del(cacheKey) await Promise.all([cache.quit(), subscriber.quit()]) await client.end({ timeout: 2 }) @@ -104,6 +126,9 @@ describe('Operator archive repair against the full compatible schema', () => { await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,archived_at) VALUES ('env','Archived','owner','owner','2026-05-01')` await client`INSERT INTO workflow (id,name,user_id,workspace_id,last_synced,created_at,updated_at,is_deployed,is_public_api) VALUES ('flow','Flow','owner','env',now(),now(),now(),true,true)` + await client`INSERT INTO workflow (id,name,user_id,workspace_id,last_synced,created_at,updated_at) + SELECT 'slow-' || n,'Slow notification ' || n,'owner','env',now(),now(),now() FROM generate_series(1,7) n + UNION ALL SELECT 'tail','Last notification','owner','env',now(),now(),now()` await client`INSERT INTO workflow_schedule (id,workflow_id,trigger_type,status,next_run_at,last_queued_at) VALUES ('schedule','flow','schedule','active',now(),now())` await client`INSERT INTO webhook (id,workflow_id,provider,path,provider_config) VALUES ('hook','flow','gitlab','fixture-hook','{}')` await client`INSERT INTO chat (id,workflow_id,user_id,identifier,title) VALUES ('chat','flow','owner','repair-chat','Chat')` @@ -113,7 +138,18 @@ describe('Operator archive repair against the full compatible schema', () => { await client`INSERT INTO mcp_servers (id,workspace_id,created_by,name,transport) VALUES (${mcpServerId},'env','owner','Cached server','streamable-http')` await cache.set(cacheKey, JSON.stringify({ tools: [], expiry: Date.now() + 60000 }), 'EX', 60) await run('plan') - await expect(run('repair')).rejects.toMatchObject({ code: 1 }) + const repair = run('repair').then( + () => null, + (error: unknown) => error + ) + try { + await expect.poll(() => tailNotified, { timeout: 10000 }).toBe(true) + expect(JSON.parse(await readFile(report, 'utf8')).status).toBe('running') + } finally { + releaseNotifications.resolve() + await repair + } + expect(await repair).toMatchObject({ code: 1 }) expect(await cache.get(cacheKey)).toBeNull() await expect .poll(() => published, { timeout: 2000 }) @@ -122,9 +158,10 @@ describe('Operator archive repair against the full compatible schema', () => { status: 'failed', repairsCompleted: [], }) - expect(await client`SELECT archived_at::text,is_deployed,is_public_api FROM workflow`).toEqual([ - { archived_at: '2026-05-01 00:00:00', is_deployed: false, is_public_api: false }, - ]) + expect(await client`SELECT id FROM workflow`).toHaveLength(9) + expect( + await client`SELECT id FROM workflow WHERE archived_at IS DISTINCT FROM '2026-05-01'::timestamp OR is_deployed OR is_public_api` + ).toHaveLength(0) expect(await client`SELECT status,next_run_at,last_queued_at FROM workflow_schedule`).toEqual([ { status: 'disabled', next_run_at: null, last_queued_at: null }, ]) diff --git a/apps/sim/lib/workspaces/lifecycle.ts b/apps/sim/lib/workspaces/lifecycle.ts index 7dad65230db..525dfb2c50f 100644 --- a/apps/sim/lib/workspaces/lifecycle.ts +++ b/apps/sim/lib/workspaces/lifecycle.ts @@ -29,7 +29,7 @@ const logger = createLogger('WorkspaceLifecycle') /** Bounds each batched workflow archive statement's parameter list. */ const WORKFLOW_ARCHIVE_BATCH_SIZE = 1_000 -/** Bounds concurrent post-commit notifications; each is best-effort and catches its own errors. */ +/** Bounds concurrent post-commit notifications and provider cleanup. */ const ARCHIVE_NOTIFICATION_CONCURRENCY = 8 /** What an environment archive must announce once its transaction commits. */ @@ -145,18 +145,22 @@ export async function finishEnvironmentArchive( ): Promise { const { workspaceId } = effects try { + const cleanupErrors: unknown[] = [] await mapWithConcurrency(effects.workflows, ARCHIVE_NOTIFICATION_CONCURRENCY, (row) => finishWorkflowArchive(row.id, workspaceId, row.serverIds, { requestId, strictExternalCleanup: options.strictExternalCleanup, - }).catch((error) => - options.strictExternalCleanup - ? Promise.reject(error) - : logger.warn(`[${requestId}] Post-archive notification failed for workflow ${row.id}`, { - error, - }) - ) + }).catch((error: unknown) => { + if (options.strictExternalCleanup) { + cleanupErrors.push(error) + return + } + logger.warn(`[${requestId}] Post-archive notification failed for workflow ${row.id}`, { + error, + }) + }) ) + if (cleanupErrors.length > 0) throw cleanupErrors[0] } finally { await mcpService.clearCache(workspaceId).catch(() => undefined) if (mcpPubSub) { diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index 344925ddd0b..f2bceb796fc 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -22,6 +22,7 @@ import postgres from 'postgres' import { z } from 'zod' const logger = createLogger('ProjectBackfillOperator', { enabled: true, logLevel: 'INFO' }) +const MAX_ARTIFACT_BYTES = 128 * 1024 * 1024 const text = z.string().min(1).max(1024) const member = z .object({ @@ -76,16 +77,19 @@ const progressSchema = z .strict() async function readJson(path: string): Promise { - if ((await stat(path)).size > 128 * 1024 * 1024) + if ((await stat(path)).size > MAX_ARTIFACT_BYTES) throw new Error('Backfill artifact exceeds 128 MiB') return JSON.parse(await readFile(path, 'utf8')) } async function writeJson(path: string, value: unknown, replace = true): Promise { + const serialized = `${JSON.stringify(value)}\n` + if (Buffer.byteLength(serialized, 'utf8') > MAX_ARTIFACT_BYTES) + throw new Error('Backfill artifact exceeds 128 MiB') const temporary = `${path}.${generateId()}.partial` const file = await open(temporary, 'wx', 0o600) try { - await file.writeFile(`${JSON.stringify(value)}\n`) + await file.writeFile(serialized) await file.sync() } finally { await file.close() diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 165c994a54d..87a7a4feb07 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -1,6 +1,6 @@ import { execFile } from 'node:child_process' import { once } from 'node:events' -import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' +import { mkdir, mkdtemp, readdir, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { promisify } from 'node:util' @@ -284,6 +284,39 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }, 60000) + it('refuses a generated plan exceeding the artifact size limit before publishing it', async () => { + await database(async (sql, url) => { + const directory = await mkdtemp(join(tmpdir(), 'project-plan-size-test-')) + try { + await sql`INSERT INTO "user" VALUES (repeat('o',1024))` + await sql`INSERT INTO workspace (id,name,owner_id) + SELECT rpad('w-' || lpad(n::text,5,'0'),1024,'x'),'Large identifier fixture',repeat('o',1024) + FROM generate_series(1,45000) n` + await expect( + promisify(execFile)( + 'bun', + [ + '--no-env-file', + 'scripts/backfill-projects.ts', + 'plan', + '--manifest', + join(directory, 'manifest.json'), + ], + { + cwd: new URL('../../../apps/sim/', import.meta.url), + env: { ...process.env, MIGRATION_DATABASE_URL: url }, + timeout: 60000, + } + ) + ).rejects.toMatchObject({ code: 1 }) + expect(await readdir(directory)).toEqual([]) + expect(await sql`SELECT id FROM project`).toHaveLength(0) + } finally { + await rm(directory, { recursive: true, force: true }) + } + }) + }, 90000) + it('gives workflow creation share locks precedence and bounds a stalled batch without retaining locks', async () => { await database(async (sql) => { await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('root','Root','owner')` From 454e2e252e69513f938cbb7d069af87c0bfd2bea Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 02:15:57 -0700 Subject: [PATCH 067/113] fix(projects): honor operator cancellation and status exits --- apps/sim/scripts/backfill-projects.ts | 12 ++- .../scripts/project-contract.integration.ts | 92 +++++++++++++++++++ 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index f2bceb796fc..b27f17c280f 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -131,7 +131,7 @@ async function main() { From apps/sim: bun --no-env-file scripts/backfill-projects.ts [options]\n plan Read-only discovery; atomically writes a new --manifest PATH (never overwrites).\nrepair Repair only archived environments listed in --manifest; requires --report PATH.\napply Assign reviewed families from --manifest; requires --report PATH.\nverify Validate the database and repair completion; requires --manifest and --report.\nstatus Read --report; does not connect or resume.\n Writes require MIGRATION_DATABASE_URL pointing directly to the primary and --ack-release-drained.\nDeploy #8830 and verify old servers/workers drained first. Keep the reviewed manifest and report\nin durable private job storage. Review conflicts and repairs before repair/apply. Repair requires\nthe app runtime environment (DATABASE_URL must target the same database) for provider cleanup.\n -Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (1–3600), --pause-ms 100 (1–60000).\nA budget limits scheduling. Transactions use 3s statement limits and a 5s total limit on PG17+,\notherwise a 5s idle-transaction limit on PG16. Reuse the same report\nto resume; lost reports can be recreated safely from the same manifest. Changed families need a\nnew plan/report. Complete verify plus deployment evidence is required before #8590 enforcement.\nExit: 0 complete, 2 paused/incomplete, 1 failure. Never run db:migrate to launch this tool.\n`) +Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (1–3600), --pause-ms 100 (1–60000).\nA budget limits scheduling. Transactions use 3s statement limits and a 5s total limit on PG17+,\notherwise a 5s idle-transaction limit on PG16. Reuse the same report\nto resume; lost reports can be recreated safely from the same manifest. Changed families need a\nnew plan/report. Complete verify plus deployment evidence is required before #8590 enforcement.\nExit: 0 complete, 2 running/paused/incomplete, 1 failure. Never run db:migrate to launch this tool.\n`) return } const [command] = positionals @@ -146,7 +146,9 @@ Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 throw new Error('Manifest and report must be different files') if (command === 'status') { if (!reportPath) throw new Error('status requires --report') - logger.info('Project backfill status', progressSchema.parse(await readJson(reportPath))) + const progress = progressSchema.parse(await readJson(reportPath)) + logger.info('Project backfill status', progress) + process.exitCode = progress.status === 'complete' ? 0 : progress.status === 'failed' ? 1 : 2 return } const rawUrl = process.env.MIGRATION_DATABASE_URL @@ -181,8 +183,10 @@ Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 const requestStop = () => { stop = true } - process.on('SIGINT', requestStop) - process.on('SIGTERM', requestStop) + if (live) { + process.on('SIGINT', requestStop) + process.on('SIGTERM', requestStop) + } let lockPid: number | undefined let repairRuntimeLoaded = false async function assertLock() { diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 87a7a4feb07..69649f56ab0 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -204,6 +204,20 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { projectsCreated: 50, status: 'paused', }) + for (const [status, exitCode] of [ + ['running', 2], + ['paused', 2], + ['incomplete', 2], + ['failed', 1], + ['complete', 0], + ] as const) { + const checkpoint = JSON.stringify({ ...report, status }) + await writeFile(reportPath, checkpoint) + const statusRun = run('status') + if (exitCode === 0) await statusRun + else await expect(statusRun).rejects.toMatchObject({ code: exitCode }) + expect(await readFile(reportPath, 'utf8')).toBe(checkpoint) + } const first = await sql`SELECT id,project_id FROM workspace WHERE project_id IS NOT NULL ORDER BY id` await rm(reportPath) @@ -284,6 +298,84 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }, 60000) + it.each([ + ['plan', 'SIGINT'], + ['verify', 'SIGTERM'], + ] as const)( + 'terminates a stalled %s scan on %s', + async (command, signal) => { + await database(async (sql, url) => { + const directory = await mkdtemp(join(tmpdir(), 'project-read-cancel-test-')) + const artifacts = [ + '--manifest', + join(directory, 'manifest.json'), + '--report', + join(directory, 'report.json'), + ] + const options = { + cwd: new URL('../../../apps/sim/', import.meta.url), + env: { ...process.env, MIGRATION_DATABASE_URL: url }, + timeout: 15000, + } + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('root','Root','owner')` + if (command === 'verify') { + for (const step of ['plan', 'apply']) + await promisify(execFile)( + 'bun', + [ + '--no-env-file', + 'scripts/backfill-projects.ts', + step, + ...artifacts, + '--ack-release-drained', + ], + options + ) + } + const files = await readdir(directory) + const contents = await Promise.all( + files.map((file) => readFile(join(directory, file), 'utf8')) + ) + await sql.unsafe(` + ALTER TABLE workspace RENAME TO workspace_fixture_source; + CREATE FUNCTION pause_fixture_scan() RETURNS boolean LANGUAGE plpgsql AS $$ + BEGIN PERFORM pg_sleep(5); RETURN true; END $$; + CREATE VIEW workspace AS SELECT * FROM workspace_fixture_source WHERE pause_fixture_scan(); + `) + const child = execFile( + 'bun', + ['--no-env-file', 'scripts/backfill-projects.ts', command, ...artifacts], + options + ) + const exited = once(child, 'close') + try { + await expect + .poll( + async () => { + const [state] = await sql`SELECT count(*)::int AS waiting FROM pg_stat_activity + WHERE datname = current_database() AND application_name = 'sim-project-backfill' + AND wait_event = 'PgSleep'` + return state.waiting + }, + { timeout: 5000, interval: 20 } + ) + .toBe(1) + child.kill(signal) + expect((await exited)[1]).toBe(signal) + expect(await readdir(directory)).toEqual(files) + expect( + await Promise.all(files.map((file) => readFile(join(directory, file), 'utf8'))) + ).toEqual(contents) + } finally { + if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL') + await exited + await rm(directory, { recursive: true, force: true }) + } + }) + }, + 20000 + ) + it('refuses a generated plan exceeding the artifact size limit before publishing it', async () => { await database(async (sql, url) => { const directory = await mkdtemp(join(tmpdir(), 'project-plan-size-test-')) From fb27e1b614ffef4362bee3529c582534a548891d Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 02:29:47 -0700 Subject: [PATCH 068/113] fix(projects): reject verification of conflicted plans --- apps/sim/scripts/backfill-projects.ts | 1 + .../scripts/project-contract.integration.ts | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index b27f17c280f..36be52e4a59 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -230,6 +230,7 @@ Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 counts.pendingCleanup = manifest.repairs.filter( (repair) => !report.repairsCompleted.includes(repair.workspaceId) ).length + counts.conflicts = manifest.conflicts.length logger.info('Project verification', { databaseId, ...counts }) if (Object.values(counts).some((count) => count !== 0)) process.exitCode = 2 return diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 69649f56ab0..9f8d6e6fc0c 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -298,6 +298,49 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }, 60000) + it('refuses a conflicted manifest even after its database invariants have been repaired', async () => { + await database(async (sql, url) => { + const directory = await mkdtemp(join(tmpdir(), 'project-conflict-verify-test-')) + const run = (command: string, plan: string) => + promisify(execFile)( + 'bun', + [ + '--no-env-file', + 'scripts/backfill-projects.ts', + command, + '--manifest', + join(directory, `${plan}-manifest.json`), + '--report', + join(directory, `${plan}-report.json`), + '--ack-release-drained', + ], + { + cwd: new URL('../../../apps/sim/', import.meta.url), + env: { ...process.env, MIGRATION_DATABASE_URL: url }, + timeout: 15000, + } + ) + try { + await sql`INSERT INTO "user" VALUES ('other-owner')` + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('root','Root','owner')` + await sql`INSERT INTO workspace (id,name,owner_id,forked_from_workspace_id) + VALUES ('child','Child','other-owner','root')` + await expect(run('plan', 'conflicted')).rejects.toMatchObject({ code: 2 }) + await expect(run('apply', 'conflicted')).rejects.toMatchObject({ code: 2 }) + await sql`UPDATE workspace SET owner_id = 'owner' WHERE id = 'child'` + await run('plan', 'repaired') + await run('apply', 'repaired') + expect(Object.values(await verifyProjectBackfill(sql)).every((count) => count === 0)).toBe( + true + ) + await run('verify', 'repaired') + await expect(run('verify', 'conflicted')).rejects.toMatchObject({ code: 2 }) + } finally { + await rm(directory, { recursive: true, force: true }) + } + }) + }, 30000) + it.each([ ['plan', 'SIGINT'], ['verify', 'SIGTERM'], From 88d10da7aafac603549f3499864858c93246776c Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 02:46:26 -0700 Subject: [PATCH 069/113] fix(projects): defer archive repair on database contention --- .../backfill-repair.integration.ts | 61 ++++++++++++++++++- apps/sim/scripts/backfill-projects.ts | 27 +++++--- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts index 139bcc53d65..5016080e1b3 100644 --- a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -46,7 +46,7 @@ const environment = { SOCKET_SERVER_URL: process.env.SOCKET_SERVER_URL, } const execute = promisify(execFile) -const run = (command: string) => +const run = (command: string, artifacts = { manifest, report }) => execute( 'bun', [ @@ -54,8 +54,8 @@ const run = (command: string) => 'scripts/backfill-projects.ts', command, '--manifest', - manifest, - ...(command === 'plan' ? [] : ['--report', report, '--ack-release-drained']), + artifacts.manifest, + ...(command === 'plan' ? [] : ['--report', artifacts.report, '--ack-release-drained']), ], { cwd: new URL('../../../', import.meta.url), @@ -121,6 +121,61 @@ afterAll(async () => { }) describe('Operator archive repair against the full compatible schema', () => { + it.each(['workspace', 'project', 'row'] as const)( + 'defers a busy %s lock, repairs unrelated environments and resumes without losing progress', + async (lock) => { + const ownerId = `owner-${lock}` + const busyId = `busy-${lock}` + const freeId = `free-${lock}` + const projectId = `project-${lock}` + const artifacts = { + manifest: join(directory, `${lock}-manifest.json`), + report: join(directory, `${lock}-report.json`), + } + const holder = postgres(url.toString(), { max: 1, onnotice: () => {} }) + try { + await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) + VALUES (${ownerId},'Owner',${`${lock}@fixture.test`},true,now(),now())` + await client`INSERT INTO project (id,name,owner_id,archived_at) + VALUES (${projectId},'Archived Project',${ownerId},'2026-05-01')` + await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id,archived_at) + VALUES (${busyId},'Busy',${ownerId},${ownerId},${projectId},'2026-05-01'), + (${freeId},'Free',${ownerId},${ownerId},NULL,'2026-05-01')` + await client`INSERT INTO workflow (id,name,user_id,workspace_id,last_synced,created_at,updated_at) + VALUES (${busyId},'Busy flow',${ownerId},${busyId},now(),now(),now()), + (${freeId},'Free flow',${ownerId},${freeId},now(),now(),now())` + await run('plan', artifacts) + await holder.begin(async (tx) => { + if (lock === 'row') await tx`SELECT id FROM workspace WHERE id = ${busyId} FOR SHARE` + else { + const key = lock === 'workspace' ? `project-backfill:${busyId}` : `project:${projectId}` + await tx`SELECT pg_advisory_xact_lock(hashtextextended(${key},0))` + } + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + expect(JSON.parse(await readFile(artifacts.report, 'utf8'))).toMatchObject({ + status: 'incomplete', + repairsCompleted: [freeId], + }) + expect(await client`SELECT workspace_id FROM workflow WHERE archived_at IS NULL`).toEqual( + [{ workspace_id: busyId }] + ) + }) + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + expect(JSON.parse(await readFile(artifacts.report, 'utf8')).repairsCompleted).toEqual( + expect.arrayContaining([freeId, busyId]) + ) + await run('apply', artifacts) + await run('verify', artifacts) + } finally { + await holder.end({ timeout: 2 }) + await client`DELETE FROM workspace WHERE owner_id = ${ownerId}` + await client`DELETE FROM project WHERE owner_id = ${ownerId}` + await client`DELETE FROM "user" WHERE id = ${ownerId}` + } + }, + 60000 + ) + it('commits the complete cascade, reports external failure and resumes cleanup from its original manifest', async () => { await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) VALUES ('owner','Owner','repair@fixture.test',true,now(),now())` await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,archived_at) VALUES ('env','Archived','owner','owner','2026-05-01')` diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index 36be52e4a59..b8d4308d548 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -331,14 +331,25 @@ Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 if (stop || attempts >= maxBatches || performance.now() - started >= seconds * 1000) break await assertLock() attempts++ - await repairArchivedProjectEnvironment(sql, repair, progress.runId) - await assertLock() - completedRepairs.add(repair.workspaceId) - await checkpoint() - logger.info('Archive repair completed', { - workspaceId: repair.workspaceId, - workflows: repair.workflowIds.length, - }) + try { + await repairArchivedProjectEnvironment(sql, repair, progress.runId) + await assertLock() + completedRepairs.add(repair.workspaceId) + await checkpoint() + logger.info('Archive repair completed', { + workspaceId: repair.workspaceId, + workflows: repair.workflowIds.length, + }) + } catch (error) { + const transient = getTransientDatabaseFailure(error) + if (!(error instanceof ProjectBackfillBusy) && !transient) throw error + await assertLock() + logger.warn('Archive repair deferred', { + workspaceId: repair.workspaceId, + failure: transient ?? 'busy', + error: describeError(error), + }) + } await sleep(pauseMs) } } else { From 4025e5a4b8490f00152470cb43e6a1cb0cca1fca Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 03:09:57 -0700 Subject: [PATCH 070/113] fix(projects): coordinate read-only maintenance commands --- apps/sim/scripts/backfill-projects.ts | 10 ++- .../scripts/project-contract.integration.ts | 77 ++++++++++++++++--- 2 files changed, 71 insertions(+), 16 deletions(-) diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index b8d4308d548..e5fdb31be4e 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -200,9 +200,14 @@ Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 } try { await assertProjectBackfillDatabase(sql, live) + const [lock] = + await sql`SELECT pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator',0)) AS held, pg_backend_pid() AS pid` + if (!lock.held) throw new Error('Another Project preparation or enforcement runner is active') + lockPid = lock.pid if (command === 'plan') { if (!manifestPath) throw new Error('plan requires --manifest') const manifest = await discoverProjectBackfill(sql, databaseId) + await assertLock() await writeJson(manifestPath, manifest, false) logger.info('Reviewable Project plan written', { databaseId, @@ -231,6 +236,7 @@ Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (repair) => !report.repairsCompleted.includes(repair.workspaceId) ).length counts.conflicts = manifest.conflicts.length + await assertLock() logger.info('Project verification', { databaseId, ...counts }) if (Object.values(counts).some((count) => count !== 0)) process.exitCode = 2 return @@ -278,10 +284,6 @@ Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 progress.deferred.some((index) => index >= manifest.families.length) ) throw new Error('Invalid report cursor') - const [lock] = - await sql`SELECT pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator',0)) AS held, pg_backend_pid() AS pid` - if (!lock.held) throw new Error('Another Project preparation or enforcement runner is active') - lockPid = lock.pid progress.runId = generateId() progress.codeHash = codeHash progress.status = 'running' diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 9f8d6e6fc0c..3d92de303f4 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -345,7 +345,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { ['plan', 'SIGINT'], ['verify', 'SIGTERM'], ] as const)( - 'terminates a stalled %s scan on %s', + 'coordinates a stalled %s scan with enforcement and terminates on %s', async (command, signal) => { await database(async (sql, url) => { const directory = await mkdtemp(join(tmpdir(), 'project-read-cancel-test-')) @@ -403,6 +403,19 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { { timeout: 5000, interval: 20 } ) .toBe(1) + const competing = await sql.reserve() + try { + const [attempt] = + await competing`SELECT pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator',0)) AS acquired` + try { + expect(attempt.acquired).toBe(false) + } finally { + if (attempt.acquired) + await competing`SELECT pg_advisory_unlock(hashtextextended('sim:project-backfill-operator',0))` + } + } finally { + competing.release() + } child.kill(signal) expect((await exited)[1]).toBe(signal) expect(await readdir(directory)).toEqual(files) @@ -491,20 +504,60 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }, 10000) - it('refuses SQL contraction while an operator holds the run lock', async () => { - await database(async (sql) => { - const connection = await sql.reserve() + it('excludes SQL contraction and database-backed commands while preserving offline status', async () => { + await database(async (sql, url) => { + const directory = await mkdtemp(join(tmpdir(), 'project-command-lock-test-')) + const manifest = join(directory, 'manifest.json') + const report = join(directory, 'report.json') + const concurrentPlan = join(directory, 'concurrent-plan.json') + const run = (command: string, plan = manifest) => + promisify(execFile)( + 'bun', + [ + '--no-env-file', + 'scripts/backfill-projects.ts', + command, + '--manifest', + plan, + '--report', + report, + '--ack-release-drained', + ], + { + cwd: new URL('../../../apps/sim/', import.meta.url), + env: { ...process.env, MIGRATION_DATABASE_URL: url }, + timeout: 15000, + } + ) try { - await connection`SELECT pg_advisory_lock(hashtextextended('sim:project-backfill-operator',0))` - await expect(applyMigration(sql, migration)).rejects.toThrow('preparation is still running') - expect(await sql`SELECT to_regclass('project_workspace')::text AS name`).toEqual([ - { name: 'project_workspace' }, - ]) + await run('plan') + await run('apply') + const connection = await sql.reserve() + try { + await connection`SELECT pg_advisory_lock(hashtextextended('sim:project-backfill-operator',0))` + await expect(applyMigration(sql, migration)).rejects.toThrow( + 'preparation is still running' + ) + const attempts = await Promise.allSettled([run('plan', concurrentPlan), run('verify')]) + expect(attempts).toMatchObject([ + { status: 'rejected', reason: { code: 1 } }, + { status: 'rejected', reason: { code: 1 } }, + ]) + await run('status') + expect(await readdir(directory)).not.toContain('concurrent-plan.json') + expect(await sql`SELECT to_regclass('project_workspace')::text AS name`).toEqual([ + { name: 'project_workspace' }, + ]) + } finally { + await connection`SELECT pg_advisory_unlock(hashtextextended('sim:project-backfill-operator',0))` + connection.release() + } + await run('plan', concurrentPlan) + await run('verify') + await applyMigration(sql, migration) } finally { - await connection`SELECT pg_advisory_unlock(hashtextextended('sim:project-backfill-operator',0))` - connection.release() + await rm(directory, { recursive: true, force: true }) } - await applyMigration(sql, migration) }) }) From ce760cca3f8261626892b9cbf7be17ed7948a49b Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 09:08:14 -0700 Subject: [PATCH 071/113] ci(projects): use the standard PostgreSQL test coverage --- .github/workflows/checks.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index df5608412ee..7210aa37488 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -121,15 +121,6 @@ jobs: bun run test --mode integration lib/billing/core/usage-log.integration.ts --outputFile.json=test-results/integration-pg16.json - - name: Verify Project column compatibility on PostgreSQL 16 - if: matrix.provision == 'push' && matrix.shard == 1 - working-directory: packages/db - env: - TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5433/sim_test - run: >- - bun run test --mode integration scripts/workspace-project-column.integration.ts scripts/push.integration.ts - --outputFile.json=test-results/integration-projects-pg16.json - - name: Upload integration test reports if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 From 8323bd3a1d42480ed0214596cd2849d2077f86cf Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 13:19:51 -0700 Subject: [PATCH 072/113] fix(projects): support trigger-free column rollout --- .../workspace-forking/lib/create-fork.test.ts | 3 + .../billing/organizations/lock-order.test.ts | 3 + .../__integration__/foundation.integration.ts | 361 ++++++++++++---- apps/sim/lib/projects/account-deletion.ts | 69 +-- .../lib/projects/application/authorization.ts | 22 +- .../sim/lib/projects/application/use-cases.ts | 19 +- apps/sim/lib/projects/environment-source.ts | 77 ++++ apps/sim/lib/projects/lifecycle.ts | 8 +- apps/sim/lib/projects/membership.ts | 74 ++-- .../0404_workspace_project_column.sql | 115 +---- .../db/migrations/meta/0404_snapshot.json | 9 + packages/db/schema.ts | 6 +- packages/db/scripts/push.integration.ts | 14 +- .../scripts/reconcile-project-membership.ts | 4 +- .../workspace-project-column.integration.ts | 406 +++--------------- 15 files changed, 579 insertions(+), 611 deletions(-) create mode 100644 apps/sim/lib/projects/environment-source.ts diff --git a/apps/sim/ee/workspace-forking/lib/create-fork.test.ts b/apps/sim/ee/workspace-forking/lib/create-fork.test.ts index 7baa904052a..eb6a2dde66e 100644 --- a/apps/sim/ee/workspace-forking/lib/create-fork.test.ts +++ b/apps/sim/ee/workspace-forking/lib/create-fork.test.ts @@ -140,6 +140,7 @@ function forkParams(selection?: { describe('createFork storage headroom gate', () => { beforeEach(() => { resetDbChainMock() + dbChainMockFns.as.mockReturnValue(workspace) queueTableRows(workspace, [{ projectId: 'project-source' }]) queueTableRows(workspace, [ { project: { id: 'project-source', organizationId: null, archivedAt: null } }, @@ -215,6 +216,7 @@ describe('createFork storage headroom gate', () => { it('refuses when the parent changed organizations after the policy was captured', async () => { resetDbChainMock() + dbChainMockFns.as.mockReturnValue(workspace) queueTableRows(workspace, [{ projectId: 'project-source' }]) queueTableRows(workspace, [ { project: { id: 'project-source', organizationId: null, archivedAt: null } }, @@ -243,6 +245,7 @@ describe('createFork storage headroom gate', () => { */ it('gives the child the source workspace personal API-key and fork-sync policies', async () => { resetDbChainMock() + dbChainMockFns.as.mockReturnValue(workspace) queueTableRows(workspace, [{ projectId: 'project-source' }]) queueTableRows(workspace, [ { project: { id: 'project-source', organizationId: null, archivedAt: null } }, diff --git a/apps/sim/lib/billing/organizations/lock-order.test.ts b/apps/sim/lib/billing/organizations/lock-order.test.ts index 4c9edc649ee..de3c565da7b 100644 --- a/apps/sim/lib/billing/organizations/lock-order.test.ts +++ b/apps/sim/lib/billing/organizations/lock-order.test.ts @@ -218,6 +218,9 @@ describe('workspace payer-change transaction lock ordering', () => { orderBy() { return chain }, + as() { + return table + }, for() { ops.push({ op: 'lock', table }) return chain diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 712824c056a..d1ddd1cd515 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -33,7 +33,10 @@ import { removeUserFromOrganization, transferOrganizationOwnership, } from '@/lib/billing/organizations/membership' -import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' +import { + getProjectAccountDeletionBlockers, + prepareProjectsForAccountDeletion, +} from '@/lib/projects/account-deletion' import { archiveProject, createProject, @@ -245,6 +248,13 @@ async function addOrganizationProject(organizationId: string, ownerId: string) { return { workspaceId, projectId } } +async function useLegacyMemberships(ids: string[], projectId: string) { + await db.transaction(async (tx) => { + await tx.update(workspace).set({ projectId: null }).where(inArray(workspace.id, ids)) + await tx.insert(projectWorkspace).values(ids.map((workspaceId) => ({ projectId, workspaceId }))) + }) +} + async function addWorkflow(workspaceId: string, userId: string) { const id = generateId() const now = new Date() @@ -294,7 +304,7 @@ afterAll(async () => { describe('Project foundation at the database and application boundary', () => { check( - 'workspace creation and fork/disconnect assign both memberships while APIs remain disabled', + 'workspace creation and fork/disconnect write only the column while APIs remain disabled', async () => { setProjectsEnabled(false) const f = await fixture(false, 1) @@ -311,9 +321,9 @@ describe('Project foundation at the database and application boundary', () => { }) ) const [sourceMembership] = await db - .select({ projectId: projectWorkspace.projectId }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, source.id)) + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(eq(workspace.id, source.id)) const [persistedSource] = await db.select().from(workspace).where(eq(workspace.id, source.id)) expect(persistedSource.projectId).toBe(sourceMembership.projectId) const parent = await getWorkspaceWithOwner(source.id) @@ -325,9 +335,9 @@ describe('Project foundation at the database and application boundary', () => { name: 'Legacy child', }) const [forkMembership] = await db - .select({ projectId: projectWorkspace.projectId }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(eq(workspace.id, fork.workspace.id)) expect(forkMembership.projectId).toBe(sourceMembership.projectId) const [persistedFork] = await db .select() @@ -337,12 +347,18 @@ describe('Project foundation at the database and application boundary', () => { await unlinkForkEdge({ parentWorkspaceId: source.id, childWorkspaceId: fork.workspace.id }) const [child] = await db.select().from(workspace).where(eq(workspace.id, fork.workspace.id)) const [detachedMembership] = await db - .select({ projectId: projectWorkspace.projectId }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, child.id)) + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(eq(workspace.id, child.id)) expect(child.forkedFromWorkspaceId).toBeNull() expect(child.projectId).toBe(detachedMembership.projectId) expect(child.projectId).not.toBe(sourceMembership.projectId) + expect( + await db + .select({ id: projectWorkspace.workspaceId }) + .from(projectWorkspace) + .where(inArray(projectWorkspace.workspaceId, [source.id, child.id])) + ).toEqual([]) expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(3) } ) @@ -401,16 +417,17 @@ describe('Project foundation at the database and application boundary', () => { name: 'Assigned child', }) const [membership] = await db - .select({ projectId: projectWorkspace.projectId, workspaceId: projectWorkspace.workspaceId }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) + .select({ projectId: workspace.projectId, workspaceId: workspace.id }) + .from(workspace) + .where(eq(workspace.id, fork.workspace.id)) expect(membership.projectId).toBe(f.projectId) await unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: fork.workspace.id }) const [detached] = await db - .select({ projectId: projectWorkspace.projectId, workspaceId: projectWorkspace.workspaceId }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, fork.workspace.id)) + .select({ projectId: workspace.projectId, workspaceId: workspace.id }) + .from(workspace) + .where(eq(workspace.id, fork.workspace.id)) expect(detached.projectId).not.toBe(f.projectId) + if (!detached.projectId) throw new Error('Missing detached Project assignment') await expect( archiveWorkspace(fork.workspace.id, { requestId: 'detached-fork-archive' }) ).resolves.toMatchObject({ archived: true }) @@ -423,7 +440,7 @@ describe('Project foundation at the database and application boundary', () => { check('legacy unassigned forks and disconnects preserve the unassigned lineage', async () => { const f = await fixture(false, 1) - await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + await db.update(workspace).set({ projectId: null }).where(eq(workspace.id, f.ids[0])) await db.delete(project).where(eq(project.id, f.projectId)) const parent = await getWorkspaceWithOwner(f.ids[0]) if (!parent) throw new Error('Missing legacy source fixture') @@ -452,8 +469,9 @@ describe('Project foundation at the database and application boundary', () => { check('fork refuses a partially assigned lineage', async () => { const f = await fixture(false, 3) await db - .delete(projectWorkspace) - .where(inArray(projectWorkspace.workspaceId, f.ids.slice(0, 2))) + .update(workspace) + .set({ projectId: null }) + .where(inArray(workspace.id, f.ids.slice(0, 2))) const parent = await getWorkspaceWithOwner(f.ids[1]) if (!parent) throw new Error('Missing source fixture') await expect( @@ -475,7 +493,7 @@ describe('Project foundation at the database and application boundary', () => { 'legacy membership decisions exclude backfill until the writing transaction commits', async () => { const f = await fixture(false, 1) - await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + await db.update(workspace).set({ projectId: null }).where(eq(workspace.id, f.ids[0])) await db.delete(project).where(eq(project.id, f.projectId)) const [legacyWorkspace] = await db.select().from(workspace).where(eq(workspace.id, f.ids[0])) expect(legacyWorkspace.projectId).toBeNull() @@ -511,25 +529,25 @@ describe('Project foundation at the database and application boundary', () => { organizationId: null, ownerId: f.ownerId, }) - await tx.insert(projectWorkspace).values({ projectId, workspaceId: f.ids[0] }) + await tx.update(workspace).set({ projectId }).where(eq(workspace.id, f.ids[0])) }) expect( await db .select({ - projectId: projectWorkspace.projectId, - workspaceId: projectWorkspace.workspaceId, + projectId: workspace.projectId, + workspaceId: workspace.id, }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, f.ids[0])) + .from(workspace) + .where(eq(workspace.id, f.ids[0])) ).toHaveLength(1) } ) check( - 'a fork waiting for an old connector writer inherits its synchronized membership', + 'a fork waiting for a legacy assignment inherits it without copying the parent column', async () => { const f = await fixture(false, 1) - await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + await db.update(workspace).set({ projectId: null }).where(eq(workspace.id, f.ids[0])) await db.delete(project).where(eq(project.id, f.projectId)) const parent = await getWorkspaceWithOwner(f.ids[0]) if (!parent) throw new Error('Missing source fixture') @@ -566,11 +584,11 @@ describe('Project foundation at the database and application boundary', () => { const [projectId, result] = await Promise.all([backfill, fork]) const [membership] = await db .select({ - projectId: projectWorkspace.projectId, - workspaceId: projectWorkspace.workspaceId, + projectId: workspace.projectId, + workspaceId: workspace.id, }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, result.workspace.id)) + .from(workspace) + .where(eq(workspace.id, result.workspace.id)) expect(membership.projectId).toBe(projectId) const [persistedFork] = await db .select() @@ -578,7 +596,7 @@ describe('Project foundation at the database and application boundary', () => { .where(eq(workspace.id, result.workspace.id)) expect(persistedFork.projectId).toBe(projectId) const [legacyParent] = await db.select().from(workspace).where(eq(workspace.id, f.ids[0])) - expect(legacyParent.projectId).toBe(projectId) + expect(legacyParent.projectId).toBeNull() const resolved = await getWorkspaceProject.execute({ principal: f.owner, input: { workspaceId: f.ids[0] }, @@ -631,11 +649,11 @@ describe('Project foundation at the database and application boundary', () => { expect( await db .select({ - projectId: projectWorkspace.projectId, - workspaceId: projectWorkspace.workspaceId, + projectId: workspace.projectId, + workspaceId: workspace.id, }) - .from(projectWorkspace) - .where(eq(projectWorkspace.projectId, created.id)) + .from(workspace) + .where(eq(workspace.projectId, created.id)) ).toEqual([{ projectId: created.id, workspaceId: env.id }]) const grants = await db.select().from(permissions).where(eq(permissions.entityId, env.id)) expect( @@ -747,9 +765,9 @@ describe('Project foundation at the database and application boundary', () => { ).toHaveLength(1) expect( await db - .select({ workspaceId: projectWorkspace.workspaceId }) - .from(projectWorkspace) - .innerJoin(project, eq(project.id, projectWorkspace.projectId)) + .select({ workspaceId: workspace.id }) + .from(workspace) + .innerJoin(project, eq(project.id, workspace.projectId)) .where(eq(project.ownerId, f.ownerId)) ).toEqual([{ workspaceId: f.ids[0] }]) expect( @@ -1053,11 +1071,11 @@ describe('Project foundation at the database and application boundary', () => { expect( await db .select({ - projectId: projectWorkspace.projectId, - workspaceId: projectWorkspace.workspaceId, + projectId: workspace.projectId, + workspaceId: workspace.id, }) - .from(projectWorkspace) - .where(eq(projectWorkspace.projectId, f.projectId)) + .from(workspace) + .where(eq(workspace.projectId, f.projectId)) ).toHaveLength(2) await expect(getProjectIssueAccess.execute(args)).rejects.toMatchObject({ code: 'conflict' }) } @@ -1126,9 +1144,9 @@ describe('Project foundation at the database and application boundary', () => { ).toEqual(f.ids.map(() => ({ projectId: f.projectId }))) expect( await db - .select({ projectId: projectWorkspace.projectId }) - .from(projectWorkspace) - .where(inArray(projectWorkspace.workspaceId, f.ids)) + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(inArray(workspace.id, f.ids)) ).toEqual(f.ids.map(() => ({ projectId: f.projectId }))) expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(1) expect(await unlinkForkEdge(edge)).toEqual({ unlinked: true }) @@ -1146,11 +1164,11 @@ describe('Project foundation at the database and application boundary', () => { } const first = await db .select({ - projectId: projectWorkspace.projectId, - workspaceId: projectWorkspace.workspaceId, + projectId: workspace.projectId, + workspaceId: workspace.id, }) - .from(projectWorkspace) - .where(inArray(projectWorkspace.workspaceId, f.ids)) + .from(workspace) + .where(inArray(workspace.id, f.ids)) const root = first.find((row) => row.workspaceId === f.ids[0]) const child = first.find((row) => row.workspaceId === f.ids[1]) const grandchild = first.find((row) => row.workspaceId === f.ids[2]) @@ -1176,11 +1194,11 @@ describe('Project foundation at the database and application boundary', () => { }) const second = await db .select({ - projectId: projectWorkspace.projectId, - workspaceId: projectWorkspace.workspaceId, + projectId: workspace.projectId, + workspaceId: workspace.id, }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, f.ids[1])) + .from(workspace) + .where(eq(workspace.id, f.ids[1])) expect(second[0].projectId).toBe(child.projectId) } ) @@ -1189,17 +1207,17 @@ describe('Project foundation at the database and application boundary', () => { 'disconnect refuses a partially assigned subtree without moving any environments', async () => { const f = await fixture(true, 3) - await db.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[2])) + await db.update(workspace).set({ projectId: null }).where(eq(workspace.id, f.ids[2])) await expect( unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: f.ids[1] }) ).rejects.toMatchObject({ code: 'conflict' }) const [child] = await db .select({ - projectId: projectWorkspace.projectId, - workspaceId: projectWorkspace.workspaceId, + projectId: workspace.projectId, + workspaceId: workspace.id, }) - .from(projectWorkspace) - .where(eq(projectWorkspace.workspaceId, f.ids[1])) + .from(workspace) + .where(eq(workspace.id, f.ids[1])) expect(child.projectId).toBe(f.projectId) const [edge] = await db.select().from(workspace).where(eq(workspace.id, f.ids[1])) expect(edge.forkedFromWorkspaceId).toBe(f.ids[0]) @@ -1354,6 +1372,7 @@ describe('Project foundation at the database and application boundary', () => { 'column reassignment waits for a legacy membership writer before locking workspace rows', async () => { const f = await fixture(false) + await useLegacyMemberships(f.ids, f.projectId) const held = createDeferred() const release = createDeferred() let replacementId = '' @@ -1393,7 +1412,7 @@ describe('Project foundation at the database and application boundary', () => { expect( await db.execute(sql`SELECT w.project_id, pw.project_id AS connector_id FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id WHERE w.id = ${f.ids[1]}`) - ).toEqual([{ project_id: replacementId, connector_id: replacementId }]) + ).toEqual([{ project_id: null, connector_id: replacementId }]) } ) @@ -1508,9 +1527,9 @@ describe('Project foundation at the database and application boundary', () => { ).toEqual([{ projectId: privateProject.projectId }]) expect( await db - .select({ workspaceId: projectWorkspace.workspaceId }) - .from(projectWorkspace) - .where(eq(projectWorkspace.projectId, privateProject.projectId)) + .select({ workspaceId: workspace.id }) + .from(workspace) + .where(eq(workspace.projectId, privateProject.projectId)) ).toEqual([{ workspaceId: privateProject.ids[0] }]) await db.transaction(async (tx) => { const projectIds = await prepareProjectsForAccountDeletion( @@ -1528,21 +1547,215 @@ describe('Project foundation at the database and application boundary', () => { } ) + check('legacy Project account preview and teardown preserve surviving environments', async () => { + const f = await fixture() + await useLegacyMemberships(f.ids, f.projectId) + if (!f.organizationId) throw new Error('Missing organization fixture') + await db.insert(member).values({ + id: generateId(), + organizationId: f.organizationId, + userId: f.teammateId, + role: 'admin', + createdAt: new Date(), + }) + expect(await getProjectAccountDeletionBlockers(f.ownerId, [])).toEqual([]) + await db.transaction(async (tx) => { + expect(await prepareProjectsForAccountDeletion(tx, f.ownerId, [])).toEqual([]) + }) + const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(record.ownerId).toBe(f.teammateId) + expect( + await db.select({ id: workspace.id }).from(workspace).where(inArray(workspace.id, f.ids)) + ).toHaveLength(2) + }) + + check( + 'legacy detach assigns the whole subtree and stale connector rows cannot restore membership', + async () => { + const f = await fixture(false, 3) + await useLegacyMemberships(f.ids, f.projectId) + await unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: f.ids[1] }) + const rows = await db.select().from(workspace).where(inArray(workspace.id, f.ids)) + const child = rows.find((row) => row.id === f.ids[1]) + const grandchild = rows.find((row) => row.id === f.ids[2]) + expect(child?.projectId).toEqual(expect.any(String)) + expect(child?.projectId).not.toBe(f.projectId) + expect(grandchild?.projectId).toBe(child?.projectId) + expect( + await db + .select({ projectId: projectWorkspace.projectId }) + .from(projectWorkspace) + .where(inArray(projectWorkspace.workspaceId, f.ids)) + ).toEqual(f.ids.map(() => ({ projectId: f.projectId }))) + const current = await getWorkspaceProject.execute({ + principal: f.owner, + input: { workspaceId: f.ids[1] }, + request, + }) + expect(current.project.id).toBe(child?.projectId) + await archiveWorkspace(f.ids[0], request) + const [original] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(original.archivedAt).not.toBeNull() + const detached = await getWorkspaceProject.execute({ + principal: f.owner, + input: { workspaceId: f.ids[2] }, + request, + }) + expect(detached.project.archivedAt).toBeNull() + } + ) + + check( + 'legacy organization deletion transfers Projects without filling their columns', + async () => { + const f = await fixture() + await useLegacyMemberships(f.ids, f.projectId) + const organizationId = f.organizationId + if (!organizationId) throw new Error('Missing organization fixture') + await db.transaction(async (tx) => { + await detachOrganizationWorkspacesTx(tx, organizationId) + await tx.delete(organization).where(eq(organization.id, organizationId)) + }) + const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(record).toMatchObject({ organizationId: null, ownerId: f.ownerId }) + expect( + await db + .select({ projectId: workspace.projectId }) + .from(workspace) + .where(inArray(workspace.id, f.ids)) + ).toEqual(f.ids.map(() => ({ projectId: null }))) + } + ) + + check( + 'Project administration includes legacy environments during partial column assignment', + async () => { + const f = await fixture(false) + await useLegacyMemberships([f.ids[1]], f.projectId) + await expect( + renameProject.execute({ + principal: f.teammate, + input: { projectId: f.projectId, name: 'Forbidden rename' }, + request, + }) + ).rejects.toMatchObject({ code: 'forbidden' }) + const visible = await getProject.execute({ + principal: f.owner, + input: { projectId: f.projectId }, + request, + }) + expect(visible.project.environments).toHaveLength(2) + } + ) + + check( + 'connector retirement refuses busy workspace or Project readers without retaining locks', + async () => { + const f = await fixture(false) + await useLegacyMemberships(f.ids, f.projectId) + for (const held of ['workspace', 'project']) { + await db.transaction(async (tx) => { + if (held === 'workspace') + expect((await lockWorkspaceProject(tx, f.ids[0]))?.id).toBe(f.projectId) + else await tx.select().from(project).where(eq(project.id, f.projectId)) + await expect( + isolated.client.begin(async (ddl) => { + await ddl`LOCK TABLE workspace, project, project_workspace IN ACCESS EXCLUSIVE MODE NOWAIT` + await ddl`DROP TABLE project_workspace` + }) + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '55P03') + await isolated.client.begin(async (writer) => { + await writer`SET LOCAL lock_timeout = '100ms'` + await writer`UPDATE workspace SET name = 'Still writable' WHERE id = ${f.ids[0]}` + }) + }) + } + } + ) + + check( + 'account teardown after a legacy detach removes only obsolete connector references', + async () => { + const f = await fixture(false) + await useLegacyMemberships(f.ids, f.projectId) + await db + .delete(permissions) + .where(and(eq(permissions.entityId, f.ids[0]), eq(permissions.userId, f.teammateId))) + await db.insert(permissions).values({ + id: generateId(), + userId: f.teammateId, + entityId: f.ids[1], + entityType: 'workspace', + permissionType: 'admin', + }) + await unlinkForkEdge({ parentWorkspaceId: f.ids[0], childWorkspaceId: f.ids[1] }) + const [detached] = await db.select().from(workspace).where(eq(workspace.id, f.ids[1])) + if (!detached.projectId) throw new Error('Missing detached Project') + await db.update(workspace).set({ projectId: f.projectId }).where(eq(workspace.id, f.ids[0])) + await expect( + db.transaction(async (tx) => { + await prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[0]]) + throw new Error('Cancel account deletion') + }) + ).rejects.toThrow('Cancel account deletion') + expect( + await db + .select({ workspaceId: projectWorkspace.workspaceId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.projectId, f.projectId)) + ).toHaveLength(2) + await db.transaction(async (tx) => { + const ids = await prepareProjectsForAccountDeletion(tx, f.ownerId, [f.ids[0]]) + expect(ids).toEqual([f.projectId]) + expect( + await tx + .select({ workspaceId: projectWorkspace.workspaceId }) + .from(projectWorkspace) + .where(eq(projectWorkspace.projectId, f.projectId)) + ).toEqual([{ workspaceId: f.ids[0] }]) + await tx.delete(workspace).where(eq(workspace.id, f.ids[0])) + await tx.delete(project).where(inArray(project.id, ids)) + }) + expect( + await db.select({ id: project.id }).from(project).where(eq(project.id, f.projectId)) + ).toEqual([]) + const [surviving] = await db.select().from(project).where(eq(project.id, detached.projectId)) + expect(surviving.ownerId).toBe(f.teammateId) + expect( + await db.select({ id: workspace.id }).from(workspace).where(eq(workspace.id, f.ids[1])) + ).toEqual([{ id: f.ids[1] }]) + } + ) + check( 'column-only application operations remain valid after the connector is removed', async () => { await clearFixtures() - await isolated.client.unsafe(` - DROP TRIGGER workspace_sync_project_membership ON workspace; - DROP TRIGGER project_workspace_sync_column ON project_workspace; - DROP FUNCTION workspace_sync_project_membership_fn(); - DROP FUNCTION project_workspace_sync_column_fn(); - DROP TABLE project_workspace; - ALTER TABLE workspace ALTER COLUMN project_id SET NOT NULL; - ALTER TABLE workspace ADD CONSTRAINT workspace_project_id_project_id_fk - FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE RESTRICT; - `) const f = await fixture(true, 1) + const locked = createDeferred() + const release = createDeferred() + const retirement = isolated.client.begin(async (tx) => { + await tx`LOCK TABLE workspace, project, project_workspace IN ACCESS EXCLUSIVE MODE NOWAIT` + const [connection] = await tx`SELECT pg_backend_pid() AS pid` + locked.resolve(connection.pid) + await release.promise + await tx`DROP TABLE project_workspace` + await tx`ALTER TABLE workspace ALTER COLUMN project_id SET NOT NULL` + await tx`ALTER TABLE workspace VALIDATE CONSTRAINT workspace_project_id_project_id_fk` + }) + await Promise.race([locked.promise, retirement]) + const overlappingRead = getWorkspaceProject.execute({ + principal: f.owner, + input: { workspaceId: f.ids[0] }, + request, + }) + try { + await waitUntilBlockedBy(await locked.promise, 'workspace') + } finally { + release.resolve() + } + const [, overlappingResult] = await Promise.all([retirement, overlappingRead]) + expect(overlappingResult.project.environments.map((row) => row.id)).toEqual(f.ids) const organizationId = f.organizationId if (!organizationId) throw new Error('Missing organization fixture') const created = await createProject.execute({ diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts index fa1c4a32df0..389ff4e1d30 100644 --- a/apps/sim/lib/projects/account-deletion.ts +++ b/apps/sim/lib/projects/account-deletion.ts @@ -3,6 +3,10 @@ import { member, permissions, project, workspace } from '@sim/db/schema' import { ORG_ADMIN_ROLES } from '@sim/platform-authz/workspace' import { and, asc, eq, inArray, ne, or, sql } from 'drizzle-orm' import type { DbOrTx, DbTransaction } from '@/lib/db/types' +import { + deleteObsoleteProjectMemberships, + getProjectEnvironmentSource, +} from '@/lib/projects/environment-source' import { lockProjectBackfillWrites, lockProjects, @@ -10,12 +14,17 @@ import { } from '@/lib/projects/membership' /** Two indexed lookups; an `OR` around a membership subquery would scan every Project. */ -async function loadRelatedProjects(executor: DbOrTx, userId: string, doomedWorkspaceIds: string[]) { +async function loadRelatedProjects( + executor: DbTransaction, + userId: string, + doomedWorkspaceIds: string[] +) { + const environments = await getProjectEnvironmentSource(executor) const doomedMemberships = doomedWorkspaceIds.length ? await executor - .select({ projectId: workspace.projectId }) - .from(workspace) - .where(inArray(workspace.id, doomedWorkspaceIds)) + .select({ projectId: environments.projectId }) + .from(environments) + .where(inArray(environments.id, doomedWorkspaceIds)) : [] const projectIds = doomedMemberships.flatMap((row) => (row.projectId ? [row.projectId] : [])) return executor @@ -100,16 +109,17 @@ interface ProjectEnvironment { } /** Every environment of `projectIds`, in one query, keyed by Project. */ -async function loadProjectEnvironments(executor: DbOrTx, projectIds: string[]) { +async function loadProjectEnvironments(executor: DbTransaction, projectIds: string[]) { + const environments = await getProjectEnvironmentSource(executor) const rows = projectIds.length ? await executor .select({ - projectId: workspace.projectId, - id: workspace.id, - archivedAt: workspace.archivedAt, + projectId: environments.projectId, + id: environments.id, + archivedAt: environments.archivedAt, }) - .from(workspace) - .where(inArray(workspace.projectId, projectIds)) + .from(environments) + .where(inArray(environments.projectId, projectIds)) : [] const byProject = new Map() for (const { projectId, ...environment } of rows) { @@ -157,25 +167,27 @@ export async function getProjectAccountDeletionBlockers( userId: string, doomedWorkspaceIds: string[] ): Promise { - const records = await loadRelatedProjects(db, userId, doomedWorkspaceIds) - const environments = await loadProjectEnvironments( - db, - records.map((record) => record.id) - ) - const doomed = new Set(doomedWorkspaceIds) - const blockers: string[] = [] - for (const record of records) { - const decision = await planProjectDeletion( - db, - record, - environments.get(record.id) ?? [], - userId, - doomed, - false + return db.transaction(async (tx) => { + const records = await loadRelatedProjects(tx, userId, doomedWorkspaceIds) + const environments = await loadProjectEnvironments( + tx, + records.map((record) => record.id) ) - if ('blocker' in decision) blockers.push(decision.blocker) - } - return blockers + const doomed = new Set(doomedWorkspaceIds) + const blockers: string[] = [] + for (const record of records) { + const decision = await planProjectDeletion( + tx, + record, + environments.get(record.id) ?? [], + userId, + doomed, + false + ) + if ('blocker' in decision) blockers.push(decision.blocker) + } + return blockers + }) } /** Transfers surviving Projects and returns private Projects to delete after their workspaces. */ @@ -235,5 +247,6 @@ export async function prepareProjectsForAccountDeletion( }) .where(eq(project.id, record.id)) } + await deleteObsoleteProjectMemberships(tx, projectsToDelete) return projectsToDelete } diff --git a/apps/sim/lib/projects/application/authorization.ts b/apps/sim/lib/projects/application/authorization.ts index cc8c193b35c..cf1b9e6ba21 100644 --- a/apps/sim/lib/projects/application/authorization.ts +++ b/apps/sim/lib/projects/application/authorization.ts @@ -1,5 +1,5 @@ import type { Principal, SessionPrincipal } from '@sim/auth/principal' -import { member, permissionGroup, permissions, project, workspace } from '@sim/db/schema' +import { member, permissionGroup, permissions, project } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { isOrgAdminRole } from '@sim/platform-authz/workspace' import { and, asc, eq, inArray, sql } from 'drizzle-orm' @@ -11,6 +11,7 @@ import { CAPABILITY_RULES, refuseCapability } from '@/lib/permission-groups/capa import { acquirePermissionGroupOrgLock } from '@/lib/permission-groups/locks' import { resolveVerifiedUserAccessControlContext } from '@/lib/permission-groups/resolve.server' import { type ProjectOperation, projectOperations } from '@/lib/projects/application/operations' +import { getProjectEnvironmentSource } from '@/lib/projects/environment-source' import { lockProject } from '@/lib/projects/membership' const logger = createLogger('ProjectAuthorization') @@ -67,23 +68,24 @@ async function loadProjectAccess( const [membership] = records.some((record) => record.organizationId) ? await (lock ? memberQuery.for('share') : memberQuery) : [] + const source = await getProjectEnvironmentSource(tx) const environments = await tx .select({ - projectId: workspace.projectId, - id: workspace.id, - name: workspace.name, - organizationId: workspace.organizationId, - archivedAt: workspace.archivedAt, - parentId: workspace.forkedFromWorkspaceId, + projectId: source.projectId, + id: source.id, + name: source.name, + organizationId: source.organizationId, + archivedAt: source.archivedAt, + parentId: source.parentId, }) - .from(workspace) + .from(source) .where( inArray( - workspace.projectId, + source.projectId, records.map((record) => record.id) ) ) - .orderBy(asc(workspace.id)) + .orderBy(asc(source.id)) const grantQuery = tx .select({ id: permissions.entityId, permission: permissions.permissionType }) .from(permissions) diff --git a/apps/sim/lib/projects/application/use-cases.ts b/apps/sim/lib/projects/application/use-cases.ts index ece290b4d1d..baf95bc7745 100644 --- a/apps/sim/lib/projects/application/use-cases.ts +++ b/apps/sim/lib/projects/application/use-cases.ts @@ -14,6 +14,7 @@ import { requireProjectPrincipal, } from '@/lib/projects/application/authorization' import { projectOperations } from '@/lib/projects/application/operations' +import { getProjectEnvironmentSource } from '@/lib/projects/environment-source' import { archiveProjectInTransaction, finishProjectArchive } from '@/lib/projects/lifecycle' import { requireProjectApiEnabled } from '@/lib/projects/rollout.server' @@ -85,6 +86,7 @@ export const listProjects: OperationUseCase< requireProjectPrincipal(principal, projectOperations.list) await requireProjectApiEnabled() return db.transaction(async (tx) => { + const environments = await getProjectEnvironmentSource(tx) /** Driven from the caller's grants and admin organization, so cost tracks their reach. */ const candidates = await tx.execute<{ id: string }>(sql` WITH accessible AS ( @@ -96,15 +98,15 @@ export const listProjects: OperationUseCase< JOIN ${workspace} ON ${workspace.organizationId} = ${member.organizationId} WHERE ${member.userId} = ${principal.userId} AND ${inArray(member.role, ORG_ADMIN_ROLES)} ) - SELECT DISTINCT ${workspace.projectId} AS id + SELECT DISTINCT ${environments.projectId} AS id FROM accessible - JOIN ${workspace} - ON ${workspace.id} = accessible.workspace_id AND ${workspace.archivedAt} IS NULL + JOIN ${environments} + ON ${environments.id} = accessible.workspace_id AND ${environments.archivedAt} IS NULL JOIN ${project} - ON ${project.id} = ${workspace.projectId} AND ${project.archivedAt} IS NULL + ON ${project.id} = ${environments.projectId} AND ${project.archivedAt} IS NULL WHERE TRUE ${input.organizationId ? sql`AND ${project.organizationId} = ${input.organizationId}` : sql``} - ${input.cursor ? sql`AND ${workspace.projectId} > ${input.cursor}` : sql``} + ${input.cursor ? sql`AND ${environments.projectId} > ${input.cursor}` : sql``} ORDER BY 1 LIMIT ${input.limit + 1} `) @@ -210,10 +212,11 @@ export const getWorkspaceProject: OperationUseCase< requireProjectPrincipal(principal, projectOperations.get) await requireProjectApiEnabled() return db.transaction(async (tx) => { + const environments = await getProjectEnvironmentSource(tx) const [membership] = await tx - .select({ projectId: workspace.projectId }) - .from(workspace) - .where(eq(workspace.id, input.workspaceId)) + .select({ projectId: environments.projectId }) + .from(environments) + .where(eq(environments.id, input.workspaceId)) .limit(1) if (!membership?.projectId) throw new OrchestrationError('not_found', 'Project not found') return { diff --git a/apps/sim/lib/projects/environment-source.ts b/apps/sim/lib/projects/environment-source.ts new file mode 100644 index 00000000000..eef59844b57 --- /dev/null +++ b/apps/sim/lib/projects/environment-source.ts @@ -0,0 +1,77 @@ +import { projectWorkspace, workspace } from '@sim/db/schema' +import { and, eq, exists, inArray, isNotNull, isNull, ne, sql } from 'drizzle-orm' +import type { SubqueryWithSelection } from 'drizzle-orm/pg-core' +import type { DbTransaction } from '@/lib/db/types' + +const fields = { + id: workspace.id, + projectId: workspace.projectId, + name: workspace.name, + ownerId: workspace.ownerId, + organizationId: workspace.organizationId, + archivedAt: workspace.archivedAt, + parentId: workspace.forkedFromWorkspaceId, +} + +/** + * Columns win over legacy assignments. The workspace table lock protects the catalog + * check and fallback read from connector retirement until the caller commits. It is + * compatible with ordinary writes; retirement locks workspace and both FK-related + * tables up front with NOWAIT before dropping the connector. + */ +async function hasLegacyMemberships(tx: DbTransaction): Promise { + await tx.execute(sql`LOCK TABLE ${workspace} IN ACCESS SHARE MODE`) + const [legacy] = await tx.execute<{ present: boolean }>( + sql`SELECT to_regclass('public.project_workspace') IS NOT NULL AS present` + ) + return legacy?.present ?? false +} + +/** Every workspace appears once; a populated column supersedes its legacy assignment. */ +export async function getProjectEnvironmentSource( + tx: DbTransaction +): Promise> { + if (!(await hasLegacyMemberships(tx))) + return tx.select(fields).from(workspace).as('project_environments') + return tx + .select(fields) + .from(workspace) + .where(isNotNull(workspace.projectId)) + .unionAll( + tx + .select({ ...fields, projectId: projectWorkspace.projectId }) + .from(workspace) + .leftJoin(projectWorkspace, eq(projectWorkspace.workspaceId, workspace.id)) + .where(isNull(workspace.projectId)) + ) + .as('project_environments') +} + +/** + * A detached environment can still have a legacy reference to a Project being deleted. + * Remove only references superseded by a different non-null column, under the caller's + * Project locks and deletion transaction. Current memberships remain protected by the FK. + */ +export async function deleteObsoleteProjectMemberships( + tx: DbTransaction, + projectIds: string[] +): Promise { + if (!projectIds.length || !(await hasLegacyMemberships(tx))) return + await tx.delete(projectWorkspace).where( + and( + inArray(projectWorkspace.projectId, projectIds), + exists( + tx + .select({ id: workspace.id }) + .from(workspace) + .where( + and( + eq(workspace.id, projectWorkspace.workspaceId), + isNotNull(workspace.projectId), + ne(workspace.projectId, projectWorkspace.projectId) + ) + ) + ) + ) + ) +} diff --git a/apps/sim/lib/projects/lifecycle.ts b/apps/sim/lib/projects/lifecycle.ts index b75d7e798c7..ea78e6c7eca 100644 --- a/apps/sim/lib/projects/lifecycle.ts +++ b/apps/sim/lib/projects/lifecycle.ts @@ -1,7 +1,8 @@ import { project, workspace } from '@sim/db/schema' -import { asc, eq } from 'drizzle-orm' +import { asc, eq, sql } from 'drizzle-orm' import { OrchestrationError } from '@/lib/core/orchestration/types' import type { DbTransaction } from '@/lib/db/types' +import { getProjectEnvironmentSource } from '@/lib/projects/environment-source' import { lockProject } from '@/lib/projects/membership' import { archiveEnvironmentInTransaction, @@ -18,10 +19,13 @@ export async function archiveProjectInTransaction( const [record] = await tx.select().from(project).where(eq(project.id, projectId)) if (!record) throw new OrchestrationError('not_found', 'Project not found') const now = record.archivedAt ?? new Date() + const source = await getProjectEnvironmentSource(tx) const environments = await tx .select({ id: workspace.id }) .from(workspace) - .where(eq(workspace.projectId, projectId)) + .where( + sql`${workspace.id} IN (SELECT ${source.id} FROM ${source} WHERE ${source.projectId} = ${projectId})` + ) .orderBy(asc(workspace.id)) .for('no key update') const effects: EnvironmentArchiveEffects[] = [] diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index b9fec536b3a..338e4230f30 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -12,6 +12,7 @@ import { import { textArrayLiteral } from '@/lib/db/arrays' import type { DbTransaction } from '@/lib/db/types' import { acquirePermissionGroupOrgLock } from '@/lib/permission-groups/locks' +import { getProjectEnvironmentSource } from '@/lib/projects/environment-source' const PROJECT_LOCK_TIMEOUT_MS = 5_000 @@ -68,7 +69,7 @@ function acquireBackfillWriteLocks(tx: DbTransaction, workspaceIds: string[]) { return acquireAdvisoryXactLocks(tx, 'project_backfill', locks) } -/** Shared per-environment gate keeps membership absence reads stable during SQL backfill. */ +/** Shared per-environment gate keeps membership absence reads stable during backfill. */ export async function lockProjectBackfillWrites( tx: DbTransaction, workspaceIds: string[] @@ -107,13 +108,15 @@ function generatedProjectName(workspaceName: string): string { } /** Selects `workspaceId` and every fork descendant, archived ones included, as `descendants`. */ -function forkSubtree(workspaceId: string): SQL { +async function forkSubtree(tx: DbTransaction, workspaceId: string): Promise { + const environments = await getProjectEnvironmentSource(tx) return sql` WITH RECURSIVE descendants AS ( - SELECT id, name, owner_id, archived_at, project_id FROM workspace WHERE id = ${workspaceId} + SELECT id, name, owner_id, archived_at, project_id FROM ${environments} WHERE id = ${workspaceId} UNION - SELECT w.id, w.name, w.owner_id, w.archived_at, w.project_id - FROM workspace w JOIN descendants d ON w.forked_from_workspace_id = d.id + SELECT ${environments.id}, ${environments.name}, ${environments.ownerId}, + ${environments.archivedAt}, ${environments.projectId} + FROM ${environments} JOIN descendants d ON ${environments.parentId} = d.id )` } @@ -137,22 +140,23 @@ export async function createProjectRecord( return id } -/** Returns null only for a legacy workspace awaiting the SQL backfill. */ +/** Returns null only when neither the column nor legacy connector assigns a Project. */ export async function lockWorkspaceProject(tx: DbTransaction, workspaceId: string) { return withProjectLockTimeout(tx, PROJECT_CHANGING, async () => { await acquireBackfillWriteLocks(tx, [workspaceId]) + const environments = await getProjectEnvironmentSource(tx) const [membership] = await tx - .select({ projectId: workspace.projectId }) - .from(workspace) - .where(eq(workspace.id, workspaceId)) + .select({ projectId: environments.projectId }) + .from(environments) + .where(eq(environments.id, workspaceId)) .limit(1) if (!membership?.projectId) return null await acquireAdvisoryXactLock(tx, 'project', projectLockKey(membership.projectId)) const [current] = await tx .select({ project }) - .from(workspace) - .innerJoin(project, eq(project.id, workspace.projectId)) - .where(eq(workspace.id, workspaceId)) + .from(environments) + .innerJoin(project, eq(project.id, environments.projectId)) + .where(eq(environments.id, workspaceId)) .limit(1) if (!current || current.project.id !== membership.projectId) { throw new ProjectConflictError('Project membership changed; retry the operation') @@ -178,15 +182,16 @@ export async function requireForkProject(tx: DbTransaction, parentWorkspaceId: s /** Legacy fallback must not hide partially assigned descendants. Caller holds the lineage lock. */ async function requireUnassignedForkSubtree(tx: DbTransaction, workspaceId: string): Promise { const descendants = await tx.execute<{ id: string }>( - sql`${forkSubtree(workspaceId)} SELECT id FROM descendants` + sql`${await forkSubtree(tx, workspaceId)} SELECT id FROM descendants` ) if (!descendants.length) return const ids = descendants.map((row) => row.id) await lockProjectBackfillWrites(tx, ids) + const environments = await getProjectEnvironmentSource(tx) const rows = await tx - .select({ id: workspace.id }) - .from(workspace) - .where(and(inArray(workspace.id, ids), isNotNull(workspace.projectId))) + .select({ id: environments.id }) + .from(environments) + .where(and(inArray(environments.id, ids), isNotNull(environments.projectId))) .limit(1) if (rows.length) throw new ProjectConflictError('Fork descendants need Project membership reconciliation') @@ -202,6 +207,7 @@ export async function archiveProjectWithLastEnvironment( workspaceId: string, now: Date ): Promise { + const environments = await getProjectEnvironmentSource(tx) const archived = await tx .update(project) .set({ archivedAt: now, updatedAt: now }) @@ -210,10 +216,10 @@ export async function archiveProjectWithLastEnvironment( eq(project.id, projectId), isNull(project.archivedAt), sql`NOT EXISTS ( - SELECT 1 FROM ${workspace} - WHERE ${workspace.projectId} = ${projectId} - AND ${workspace.id} <> ${workspaceId} - AND ${workspace.archivedAt} IS NULL + SELECT 1 FROM ${environments} + WHERE ${environments.projectId} = ${projectId} + AND ${environments.id} <> ${workspaceId} + AND ${environments.archivedAt} IS NULL )` ) ) @@ -241,7 +247,7 @@ export async function splitForkProject( owner_id: string archived_at: Date | null project_id: string | null - }>(sql`${forkSubtree(workspaceId)} SELECT * FROM descendants`) + }>(sql`${await forkSubtree(tx, workspaceId)} SELECT * FROM descendants`) if (rows.some((row) => row.project_id !== owner.id)) throw new ProjectConflictError( 'Fork descendants need Project membership reconciliation before disconnecting' @@ -250,14 +256,15 @@ export async function splitForkProject( if (!root || rows.every((row) => row.archived_at)) throw new ProjectConflictError('A new Project needs an active environment') const ids = rows.map((row) => row.id) + const environments = await getProjectEnvironmentSource(tx) const [remaining] = await tx - .select({ id: workspace.id }) - .from(workspace) + .select({ id: environments.id }) + .from(environments) .where( and( - eq(workspace.projectId, owner.id), - isNull(workspace.archivedAt), - notInArray(workspace.id, ids) + eq(environments.projectId, owner.id), + isNull(environments.archivedAt), + notInArray(environments.id, ids) ) ) .limit(1) @@ -307,19 +314,20 @@ export async function transferWorkspaceProjects( ): Promise { if (!workspaceIds.length) return await lockProjectBackfillWrites(tx, workspaceIds) + const environments = await getProjectEnvironmentSource(tx) const owners = await tx - .selectDistinct({ id: workspace.projectId }) - .from(workspace) - .where(inArray(workspace.id, workspaceIds)) - .orderBy(asc(workspace.projectId)) + .selectDistinct({ id: environments.projectId }) + .from(environments) + .where(inArray(environments.id, workspaceIds)) + .orderBy(asc(environments.projectId)) const projectIds = owners.flatMap((row) => (row.id ? [row.id] : [])) if (!projectIds.length) return await tryLockProjects(tx, projectIds) const selected = new Set(workspaceIds) const members = await tx - .select({ id: workspace.id }) - .from(workspace) - .where(inArray(workspace.projectId, projectIds)) + .select({ id: environments.id }) + .from(environments) + .where(inArray(environments.projectId, projectIds)) if (members.some((row) => !selected.has(row.id))) { throw new ProjectConflictError( 'Move all environments in the Project together, or disconnect the fork first' diff --git a/packages/db/migrations/0404_workspace_project_column.sql b/packages/db/migrations/0404_workspace_project_column.sql index 563c917e196..a7d90ceec3a 100644 --- a/packages/db/migrations/0404_workspace_project_column.sql +++ b/packages/db/migrations/0404_workspace_project_column.sql @@ -4,50 +4,18 @@ BEGIN; SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true); SET LOCAL statement_timeout = '5s'; --- Install both directions together, without queuing behind a live writer. LOCK TABLE workspace IN ACCESS EXCLUSIVE MODE NOWAIT; -LOCK TABLE project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT; +LOCK TABLE project IN SHARE ROW EXCLUSIVE MODE NOWAIT; ALTER TABLE "workspace" ADD COLUMN IF NOT EXISTS "project_id" text; - -CREATE OR REPLACE FUNCTION workspace_sync_project_membership_fn() RETURNS trigger LANGUAGE plpgsql AS $$ -BEGIN - IF TG_OP = 'UPDATE' AND OLD.project_id IS NOT DISTINCT FROM NEW.project_id THEN - RETURN NEW; - END IF; - IF NEW.project_id IS NULL THEN - DELETE FROM project_workspace WHERE workspace_id = NEW.id; - ELSE - INSERT INTO project_workspace (project_id, workspace_id) VALUES (NEW.project_id, NEW.id) - ON CONFLICT (workspace_id) DO UPDATE SET project_id = EXCLUDED.project_id - WHERE project_workspace.project_id IS DISTINCT FROM EXCLUDED.project_id; - END IF; - RETURN NEW; -END; -$$; -CREATE OR REPLACE TRIGGER workspace_sync_project_membership - AFTER INSERT OR UPDATE OF project_id ON workspace - FOR EACH ROW EXECUTE FUNCTION workspace_sync_project_membership_fn(); - -CREATE OR REPLACE FUNCTION project_workspace_sync_column_fn() RETURNS trigger LANGUAGE plpgsql AS $$ -BEGIN - IF TG_OP = 'DELETE' THEN - UPDATE workspace SET project_id = NULL WHERE id = OLD.workspace_id AND project_id IS NOT NULL; - RETURN OLD; - END IF; - IF TG_OP = 'UPDATE' AND OLD.workspace_id IS DISTINCT FROM NEW.workspace_id THEN - RAISE EXCEPTION 'Project membership workspace identity cannot change' USING ERRCODE = '55000'; +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint + WHERE conrelid = 'workspace'::regclass AND conname = 'workspace_project_id_project_id_fk') THEN + ALTER TABLE "workspace" ADD CONSTRAINT "workspace_project_id_project_id_fk" + FOREIGN KEY ("project_id") REFERENCES "project"("id") ON DELETE RESTRICT NOT VALID; END IF; - UPDATE workspace SET project_id = NEW.project_id - WHERE id = NEW.workspace_id AND project_id IS DISTINCT FROM NEW.project_id; - RETURN NEW; -END; -$$; -CREATE OR REPLACE TRIGGER project_workspace_sync_column - AFTER INSERT OR UPDATE OR DELETE ON project_workspace - FOR EACH ROW EXECUTE FUNCTION project_workspace_sync_column_fn(); +END $$; COMMIT; --> statement-breakpoint --- This covers index construction and the whole copy CALL; bounded copy batches commit independently. SET statement_timeout = '15min'; --> statement-breakpoint SET lock_timeout = 0; @@ -57,75 +25,6 @@ DROP INDEX CONCURRENTLY IF EXISTS "workspace_project_id_id_idx"; --> statement-breakpoint CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_project_id_id_idx" ON "workspace" USING btree ("project_id","id"); --> statement-breakpoint -SET lock_timeout = '1s'; ---> statement-breakpoint -CREATE OR REPLACE PROCEDURE pg_temp.copy_workspace_projects() LANGUAGE plpgsql AS $$ -DECLARE - previous_id text; - batch_ids text[]; - attempts integer := 0; - copied integer := 0; - changed integer; - retry boolean; -BEGIN - LOOP - SELECT array_agg(id ORDER BY id COLLATE "C") INTO batch_ids FROM ( - SELECT w.id FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE (previous_id IS NULL OR w.id COLLATE "C" > previous_id COLLATE "C") - AND w.project_id IS DISTINCT FROM pw.project_id - ORDER BY w.id COLLATE "C" LIMIT 100 - ) batch; - EXIT WHEN batch_ids IS NULL; - PERFORM set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL - THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true); - retry := false; - BEGIN - PERFORM id FROM workspace WHERE id = ANY(batch_ids) ORDER BY id COLLATE "C" - FOR NO KEY UPDATE NOWAIT; - -- Old writers can hold connector rows first: release the entire batch instead of waiting. - PERFORM workspace_id FROM project_workspace WHERE workspace_id = ANY(batch_ids) - ORDER BY workspace_id COLLATE "C" FOR UPDATE NOWAIT; - -- Read again after locking so a concurrent detach cannot be overwritten by discovery's snapshot. - UPDATE workspace w SET project_id = pw.project_id FROM project_workspace pw - WHERE w.id = ANY(batch_ids) AND pw.workspace_id = w.id - AND w.project_id IS DISTINCT FROM pw.project_id; - GET DIAGNOSTICS changed = ROW_COUNT; - copied := copied + changed; - EXCEPTION WHEN lock_not_available OR deadlock_detected OR serialization_failure THEN - retry := true; - END; - COMMIT; - IF retry THEN - attempts := attempts + 1; - IF attempts >= 20 THEN - RAISE EXCEPTION 'Project column copy remains busy; safe to retry migration' - USING ERRCODE = '55P03', DETAIL = batch_ids[1]; - END IF; - PERFORM pg_sleep(0.05 + random() * 0.15); - COMMIT; - ELSE - attempts := 0; - previous_id := batch_ids[cardinality(batch_ids)]; - END IF; - END LOOP; - RAISE NOTICE 'Project column copy complete: % environments copied', copied; -END; -$$; ---> statement-breakpoint -CALL pg_temp.copy_workspace_projects(); ---> statement-breakpoint -DROP PROCEDURE pg_temp.copy_workspace_projects(); ---> statement-breakpoint -DO $$ BEGIN - IF EXISTS ( - SELECT 1 FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE w.project_id IS DISTINCT FROM pw.project_id - ) THEN - RAISE EXCEPTION 'Project column and connector assignments disagree; reconcile before retrying' - USING ERRCODE = '55000'; - END IF; -END $$; ---> statement-breakpoint SET lock_timeout = '5s'; --> statement-breakpoint SET statement_timeout = 0; diff --git a/packages/db/migrations/meta/0404_snapshot.json b/packages/db/migrations/meta/0404_snapshot.json index e1a1bf36593..264a1375ce6 100644 --- a/packages/db/migrations/meta/0404_snapshot.json +++ b/packages/db/migrations/meta/0404_snapshot.json @@ -27171,6 +27171,15 @@ "onDelete": "set null", "onUpdate": "no action" }, + "workspace_project_id_project_id_fk": { + "name": "workspace_project_id_project_id_fk", + "tableFrom": "workspace", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, "workspace_billed_account_user_id_user_id_fk": { "name": "workspace_billed_account_user_id_user_id_fk", "tableFrom": "workspace", diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 645a3402f05..cafd917b654 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2018,8 +2018,8 @@ export const workspace = pgTable( organizationId: text('organization_id').references(() => organization.id, { onDelete: 'set null', }), - /** Nullable and unconstrained until older connector-only writers retire. */ - projectId: text('project_id'), + /** Legacy memberships remain nullable until the registered Project backfill completes. */ + projectId: text('project_id').references(() => project.id, { onDelete: 'restrict' }), workspaceMode: workspaceModeEnum('workspace_mode').notNull().default('grandfathered_shared'), billedAccountUserId: text('billed_account_user_id') .notNull() @@ -2117,7 +2117,7 @@ export const project = pgTable( export const projectWorkspace = pgTable( 'project_workspace', { - // contract-pending(after #8830 is fully deployed and pre-8830 servers/workers drain): remove this table and its synchronization triggers in #8590 — application membership reads and writes use workspace.projectId. + // contract-pending(after #8830 is fully deployed and pre-8830 servers/workers drain): retire this table in #8590 — new writes use workspace.projectId and compatibility readers tolerate its absence. /** @deprecated Use workspace.projectId; retained for pre-8830 binaries during rollout. */ projectId: text('project_id') .notNull() diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index 23ce813bfb9..3c2c5d56879 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -185,7 +185,7 @@ export const knowledgeBases = pgTable('knowledge_base', { 30_000 ) - it('preserves legacy Project assignments and synchronizes both writers through schema push and replay', async () => { + it('preserves legacy assignments without copying or synchronizing through schema push and replay', async () => { await sql`CREATE TABLE project (id text PRIMARY KEY)` await sql`CREATE TABLE workspace (id text PRIMARY KEY, forked_from_workspace_id text)` await sql`CREATE TABLE project_workspace (project_id text NOT NULL, workspace_id text NOT NULL CONSTRAINT project_workspace_workspace_id_unique UNIQUE)` @@ -206,9 +206,9 @@ export const memberships = pgTable('project_workspace', { await sql`SELECT id, project_id FROM workspace ORDER BY id`, first.stdout + first.stderr ).toEqual([ - { id: 'fork', project_id: 'family' }, - { id: 'root', project_id: 'family' }, - { id: 'standalone', project_id: 'singleton' }, + { id: 'fork', project_id: null }, + { id: 'root', project_id: null }, + { id: 'standalone', project_id: null }, ]) await sql`UPDATE project_workspace SET project_id = 'singleton' WHERE workspace_id = 'fork'` await sql`UPDATE workspace SET project_id = 'family' WHERE id = 'standalone'` @@ -218,9 +218,9 @@ export const memberships = pgTable('project_workspace', { await sql`SELECT w.id, w.project_id, pw.project_id AS legacy FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` ).toEqual([ - { id: 'fork', project_id: 'singleton', legacy: 'singleton' }, - { id: 'root', project_id: 'family', legacy: 'family' }, - { id: 'standalone', project_id: 'family', legacy: 'family' }, + { id: 'fork', project_id: null, legacy: 'singleton' }, + { id: 'root', project_id: null, legacy: 'family' }, + { id: 'standalone', project_id: 'family', legacy: 'singleton' }, ]) }, 60_000) diff --git a/packages/db/scripts/reconcile-project-membership.ts b/packages/db/scripts/reconcile-project-membership.ts index f16aebc1ade..6f5d9a51872 100644 --- a/packages/db/scripts/reconcile-project-membership.ts +++ b/packages/db/scripts/reconcile-project-membership.ts @@ -23,7 +23,7 @@ try { ) } if (!process.argv.includes('--prepare') && state.workspace && state.project && state.connector) { - // Reuse the migration's bounded copy and bridge so push cannot drift from deployed upgrades. + // Reuse additive DDL so schema push preserves the same legacy membership state as upgrades. const source = await readFile( new URL('../migrations/0404_workspace_project_column.sql', import.meta.url), 'utf8' @@ -36,7 +36,7 @@ try { } finally { connection.release() } - logger.info('Project membership copy and synchronization completed') + logger.info('Nullable Project membership column prepared') } } catch (error) { logger.error('Project schema push stopped; resolve the error and retry', { diff --git a/packages/db/scripts/workspace-project-column.integration.ts b/packages/db/scripts/workspace-project-column.integration.ts index 421229c2afb..046345e0c0f 100644 --- a/packages/db/scripts/workspace-project-column.integration.ts +++ b/packages/db/scripts/workspace-project-column.integration.ts @@ -30,412 +30,146 @@ async function fixture(run: (sql: postgres.Sql) => Promise): Promise }) } -async function installBridgeBeforeCopy(sql: postgres.Sql): Promise { - for (const statement of migration.split('--> statement-breakpoint')) { - if (statement.trim().startsWith('CALL pg_temp.copy_workspace_projects()')) return - if (statement.trim()) await sql.unsafe(statement) - } - throw new Error('Migration has no Project copy entry point') -} - -async function withConnections( - sql: postgres.Sql, - run: (writer: postgres.Sql, fork: postgres.Sql, observer: postgres.Sql) => Promise -): Promise { - const [scope] = await sql<{ schema: string }[]>`SELECT current_schema() AS schema` - if (!scope) throw new Error('Migration fixture has no current schema') - const writer = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => {} }) - const fork = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => {} }) - const observer = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => {} }) - try { - for (const connection of [writer, fork, observer]) { - await connection`SET search_path = ${connection(scope.schema)}` - await connection`SET statement_timeout = '10s'` - } - await run(writer, fork, observer) - } finally { - await Promise.all([writer.end(), fork.end(), observer.end()]) - } -} - -async function backendPid(sql: postgres.Sql): Promise { - const [backend] = await sql<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` - if (!backend) throw new Error('Migration fixture connection has no backend PID') - return backend.pid -} - -async function expectBlockedBy( - observer: postgres.Sql, - waitingPid: number, - blockingPid: number -): Promise { - await expect - .poll( - async () => { - const [state] = await observer<{ waiting: boolean }[]>` - SELECT ${blockingPid} = ANY(pg_blocking_pids(${waitingPid})) AS waiting` - return state?.waiting - }, - { timeout: 5_000 } - ) - .toBe(true) -} - describe('workspace Project column compatibility migration', () => { - it('copies existing memberships and permits old workspace-before-Project creation', async () => { + it('leaves legacy rows unassigned and accepts old workspace-before-Project creation', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) - await sql.begin(async (tx) => { await tx`INSERT INTO workspace (id) VALUES ('old-writer')` await tx`INSERT INTO project VALUES ('new-project')` await tx`INSERT INTO project_workspace VALUES ('new-project', 'old-writer')` }) - - expect( - await sql`SELECT w.id, w.project_id, pw.project_id AS membership, w.forked_from_workspace_id AS parent - FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` - ).toEqual([ - { id: 'old-writer', project_id: 'new-project', membership: 'new-project', parent: null }, - { id: 'personal', project_id: 'private', membership: 'private', parent: null }, - { id: 'production', project_id: 'family', membership: 'family', parent: null }, - { id: 'staging', project_id: 'family', membership: 'family', parent: 'production' }, + expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'old-writer', project_id: null }, + { id: 'personal', project_id: null }, + { id: 'production', project_id: null }, + { id: 'staging', project_id: null }, ]) + expect( + await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'old-writer'` + ).toEqual([{ project_id: 'new-project' }]) }) }) - it('mirrors old connector reassignment and removal while rejecting workspace identity changes', async () => { + it('new column writes do not create or overwrite legacy connector assignments', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) - await sql`INSERT INTO workspace (id) VALUES ('moved-target')` - await sql`UPDATE project_workspace SET project_id = 'private' WHERE workspace_id = 'staging'` - expect(await sql`SELECT project_id FROM workspace WHERE id = 'staging'`).toEqual([ - { project_id: 'private' }, - ]) - - await expect(sql`UPDATE project_workspace SET workspace_id = 'moved-target' - WHERE workspace_id = 'staging'`).rejects.toSatisfy( - (error: unknown) => getPostgresErrorCode(error) === '55000' - ) + await sql`INSERT INTO workspace (id, project_id) VALUES ('new-writer', 'private')` + await sql`UPDATE workspace SET project_id = 'private', forked_from_workspace_id = NULL + WHERE id = 'staging'` expect( - await sql`SELECT id, project_id FROM workspace WHERE id IN ('staging', 'moved-target') ORDER BY id` + await sql`SELECT project_id, workspace_id FROM project_workspace ORDER BY workspace_id` ).toEqual([ - { id: 'moved-target', project_id: null }, - { id: 'staging', project_id: 'private' }, + { project_id: 'private', workspace_id: 'personal' }, + { project_id: 'family', workspace_id: 'production' }, + { project_id: 'family', workspace_id: 'staging' }, ]) - expect( - await sql`SELECT project_id, workspace_id FROM project_workspace WHERE workspace_id = 'staging'` - ).toEqual([{ project_id: 'private', workspace_id: 'staging' }]) - - await sql`DELETE FROM project_workspace WHERE workspace_id = 'staging'` - expect(await sql`SELECT project_id FROM workspace WHERE id = 'staging'`).toEqual([ + await sql`UPDATE project_workspace SET project_id = 'family' WHERE workspace_id = 'personal'` + expect(await sql`SELECT project_id FROM workspace WHERE id = 'personal'`).toEqual([ { project_id: null }, ]) - expect(await sql`SELECT workspace_id FROM project_workspace ORDER BY workspace_id`).toEqual([ - { workspace_id: 'personal' }, - { workspace_id: 'production' }, - ]) - }) - }) - - it('mirrors new column writes, fork detach and workspace cascades into old readers', async () => { - await fixture(async (sql) => { - await applyMigration(sql, migration) - await sql`INSERT INTO workspace (id, project_id, forked_from_workspace_id) - VALUES ('new-fork', 'family', 'production')` - expect( - await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'new-fork'` - ).toEqual([{ project_id: 'family' }]) - await sql`UPDATE workspace SET project_id = 'private' WHERE id = 'new-fork'` - expect( - await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'new-fork'` - ).toEqual([{ project_id: 'private' }]) - - await sql`UPDATE workspace SET project_id = NULL, forked_from_workspace_id = NULL - WHERE id = 'new-fork'` - expect( - await sql`SELECT workspace_id FROM project_workspace WHERE workspace_id = 'new-fork'` - ).toEqual([]) - expect( - await sql`SELECT project_id, forked_from_workspace_id FROM workspace WHERE id = 'new-fork'` - ).toEqual([{ project_id: null, forked_from_workspace_id: null }]) - - await sql`DELETE FROM workspace WHERE id = 'production'` - expect(await sql`SELECT workspace_id FROM project_workspace ORDER BY workspace_id`).toEqual([ - { workspace_id: 'personal' }, - { workspace_id: 'staging' }, - ]) - expect( - await sql`SELECT project_id, forked_from_workspace_id FROM workspace WHERE id = 'staging'` - ).toEqual([{ project_id: 'family', forked_from_workspace_id: null }]) }) }) - it('rejects missing Projects atomically and rolls back both representations with the writer', async () => { + it('rejects dangling new assignments and protects column-only Projects from old teardown', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) await expect( - sql`INSERT INTO workspace (id, project_id) VALUES ('valid-new', 'family'), ('invalid-new', 'missing')` + sql`INSERT INTO workspace (id, project_id) VALUES ('invalid', 'missing')` ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23503') - expect(await sql`SELECT id FROM workspace WHERE id IN ('valid-new', 'invalid-new')`).toEqual( - [] - ) - expect( - await sql`SELECT workspace_id FROM project_workspace WHERE workspace_id = 'valid-new'` - ).toEqual([]) - + await sql`INSERT INTO project VALUES ('column-only')` + await sql`INSERT INTO workspace (id, project_id) VALUES ('new-writer', 'column-only')` await expect( sql.begin(async (tx) => { - await tx`UPDATE workspace SET project_id = 'private' WHERE id = 'staging'` - await tx`UPDATE workspace SET project_id = 'missing' WHERE id = 'production'` + await tx`DELETE FROM project_workspace WHERE project_id = 'column-only'` + await tx`DELETE FROM project WHERE id = 'column-only'` }) ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23503') - expect( - await sql`SELECT w.id, w.project_id, pw.project_id AS membership - FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE w.id IN ('production', 'staging') ORDER BY w.id` - ).toEqual([ - { id: 'production', project_id: 'family', membership: 'family' }, - { id: 'staging', project_id: 'family', membership: 'family' }, + expect(await sql`SELECT id FROM project WHERE id = 'column-only'`).toEqual([ + { id: 'column-only' }, + ]) + expect(await sql`SELECT project_id FROM workspace WHERE id = 'new-writer'`).toEqual([ + { project_id: 'column-only' }, ]) }) }) - it('replays without restoring a detached fork or overwriting a newer Project assignment', async () => { + it('keeps old teardown valid for untouched legacy workspaces', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) - await sql`UPDATE workspace SET project_id = 'private' WHERE id = 'production'` - await sql`UPDATE workspace SET project_id = NULL, forked_from_workspace_id = NULL - WHERE id = 'staging'` - - await applyMigration(sql, migration) - await applyMigration(sql, migration) - - expect( - await sql`SELECT w.id, w.project_id, pw.project_id AS membership, w.forked_from_workspace_id AS parent - FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` - ).toEqual([ - { id: 'personal', project_id: 'private', membership: 'private', parent: null }, - { id: 'production', project_id: 'private', membership: 'private', parent: null }, - { id: 'staging', project_id: null, membership: null, parent: null }, - ]) - - await sql`INSERT INTO project_workspace VALUES ('family', 'staging')` - expect(await sql`SELECT project_id FROM workspace WHERE id = 'staging'`).toEqual([ - { project_id: 'family' }, + await sql.begin(async (tx) => { + await tx`DELETE FROM project_workspace WHERE project_id = 'private'` + await tx`DELETE FROM project WHERE id = 'private'` + await tx`DELETE FROM workspace WHERE id = 'personal'` + }) + expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'production', project_id: null }, + { id: 'staging', project_id: null }, ]) }) }) - it('allows several short copy batches to exceed one transaction timeout in total', async () => { + it('replays without copying stale legacy assignments over columns or detached forks', async () => { await fixture(async (sql) => { - await sql`ALTER TABLE workspace ADD COLUMN project_id text` - await sql`INSERT INTO workspace (id) - SELECT 'batch-' || lpad(n::text, 4, '0') FROM generate_series(1, 251) n` - await sql`INSERT INTO project_workspace (project_id, workspace_id) - SELECT 'family', id FROM workspace WHERE id LIKE 'batch-%'` - await sql.unsafe(`CREATE FUNCTION delay_copy_batch() RETURNS trigger LANGUAGE plpgsql AS $$ - BEGIN - IF NEW.id IN ('batch-0001', 'batch-0101', 'batch-0201') THEN PERFORM pg_sleep(2); END IF; - RETURN NEW; - END; - $$`) - await sql`CREATE TRIGGER delay_copy_batch BEFORE UPDATE OF project_id ON workspace - FOR EACH ROW EXECUTE FUNCTION delay_copy_batch()` await applyMigration(sql, migration) - expect( - await sql`SELECT count(*)::int AS count FROM workspace w - JOIN project_workspace pw ON pw.workspace_id = w.id AND pw.project_id = w.project_id` - ).toEqual([{ count: 254 }]) - }) - }, 20000) - - it('commits bounded copy batches, retains completed work on failure and safely resumes', async () => { - await fixture(async (sql) => { - await sql`ALTER TABLE workspace ADD COLUMN project_id text` - await sql`INSERT INTO workspace (id) - SELECT 'batch-' || lpad(n::text, 4, '0') FROM generate_series(1, 251) n` - await sql`INSERT INTO project_workspace (project_id, workspace_id) - SELECT 'family', id FROM workspace WHERE id LIKE 'batch-%'` - await sql`CREATE TABLE copy_commit_audit (workspace_id text PRIMARY KEY, transaction_id bigint NOT NULL)` - await sql.unsafe(`CREATE FUNCTION record_copy_transaction() RETURNS trigger LANGUAGE plpgsql AS $$ - BEGIN - IF NEW.project_id IS NOT NULL AND OLD.project_id IS DISTINCT FROM NEW.project_id THEN - INSERT INTO copy_commit_audit VALUES (NEW.id, txid_current()) ON CONFLICT DO NOTHING; - END IF; - RETURN NEW; - END; - $$`) - await sql`CREATE TRIGGER record_copy_transaction AFTER UPDATE OF project_id ON workspace - FOR EACH ROW EXECUTE FUNCTION record_copy_transaction()` - await sql`ALTER TABLE workspace ADD CONSTRAINT reject_later_copy - CHECK (id <> 'batch-0201' OR project_id IS NULL)` - - await expect(applyMigration(sql, migration)).rejects.toSatisfy( - (error: unknown) => getPostgresErrorCode(error) === '23514' - ) - expect( - await sql`SELECT count(*)::int AS count FROM workspace WHERE project_id IS NOT NULL` - ).toEqual([{ count: 200 }]) - expect( - await sql`SELECT count(*)::int AS count FROM copy_commit_audit GROUP BY transaction_id ORDER BY transaction_id` - ).toEqual([{ count: 100 }, { count: 100 }]) - expect(await sql`SELECT project_id FROM workspace WHERE id = 'batch-0201'`).toEqual([ - { project_id: null }, - ]) - - await sql`ALTER TABLE workspace DROP CONSTRAINT reject_later_copy` - await sql`UPDATE project_workspace SET project_id = 'private' WHERE workspace_id = 'batch-0001'` - await sql`DELETE FROM project_workspace WHERE workspace_id = 'batch-0002'` + await sql`UPDATE workspace SET project_id = 'private', forked_from_workspace_id = NULL + WHERE id = 'staging'` await applyMigration(sql, migration) await applyMigration(sql, migration) - expect( - await sql`SELECT w.id, w.project_id, pw.project_id AS membership FROM workspace w - LEFT JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE w.id IN ('batch-0001', 'batch-0002', 'batch-0201', 'batch-0251') ORDER BY w.id` + await sql`SELECT w.id, w.project_id, pw.project_id AS legacy, + w.forked_from_workspace_id AS parent FROM workspace w + JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` ).toEqual([ - { id: 'batch-0001', project_id: 'private', membership: 'private' }, - { id: 'batch-0002', project_id: null, membership: null }, - { id: 'batch-0201', project_id: 'family', membership: 'family' }, - { id: 'batch-0251', project_id: 'family', membership: 'family' }, + { id: 'personal', project_id: null, legacy: 'private', parent: null }, + { id: 'production', project_id: null, legacy: 'family', parent: null }, + { id: 'staging', project_id: 'private', legacy: 'family', parent: null }, ]) - expect( - await sql`SELECT count(*)::int AS count FROM workspace w - LEFT JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE w.project_id IS DISTINCT FROM pw.project_id` - ).toEqual([{ count: 0 }]) - expect( - await sql`SELECT count(*)::int AS count FROM copy_commit_audit GROUP BY transaction_id ORDER BY transaction_id` - ).toEqual([{ count: 100 }, { count: 100 }, { count: 54 }]) }) }) - it.each([ - { change: 'legacy reassignment', projectId: 'private' }, - { change: 'legacy removal', projectId: null }, - ])( - 'preserves $change and a waiting fork detach during concurrent copy', - async ({ projectId }) => { - await fixture(async (sql) => { - await installBridgeBeforeCopy(sql) - await sql`UPDATE workspace SET project_id = 'family' WHERE id = 'staging'` - await withConnections(sql, async (writer, fork, observer) => { - const writerPid = await backendPid(writer) - const forkPid = await backendPid(fork) - const copyPid = await backendPid(sql) - let forkLock: Promise | undefined - let copy: Promise | undefined - await writer.unsafe('BEGIN') - await fork.unsafe('BEGIN') - try { - await writer`SELECT pg_advisory_xact_lock(hashtextextended('project:family', 0))` - if (projectId === null) { - await writer`DELETE FROM project_workspace WHERE workspace_id = 'production'` - } else { - await writer`UPDATE project_workspace SET project_id = ${projectId} - WHERE workspace_id = 'production'` - } - forkLock = - fork`SELECT pg_advisory_xact_lock(hashtextextended('project:family', 0))`.then( - () => undefined - ) - void forkLock.catch(() => undefined) - await expectBlockedBy(observer, forkPid, writerPid) - - copy = sql.unsafe('CALL pg_temp.copy_workspace_projects()').then(() => undefined) - void copy.catch(() => undefined) - await expect - .poll( - async () => { - const [activity] = await observer<{ wait_event: string | null }[]>` - SELECT wait_event FROM pg_stat_activity WHERE pid = ${copyPid}` - return activity?.wait_event - }, - { timeout: 5_000 } - ) - .toBe('PgSleep') - - await writer.unsafe('COMMIT') - await forkLock - await fork`UPDATE workspace SET project_id = NULL, forked_from_workspace_id = NULL - WHERE id = 'staging'` - await fork.unsafe('COMMIT') - await copy - - expect( - await observer`SELECT w.id, w.project_id, pw.project_id AS membership, - w.forked_from_workspace_id AS parent FROM workspace w - LEFT JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE w.id IN ('production', 'staging') ORDER BY w.id` - ).toEqual([ - { id: 'production', project_id: projectId, membership: projectId, parent: null }, - { id: 'staging', project_id: null, membership: null, parent: null }, - ]) - } finally { - await writer.unsafe('ROLLBACK') - await forkLock?.catch(() => undefined) - await fork.unsafe('ROLLBACK') - await copy?.catch(() => undefined) - } - }) - }) - } - ) - - it('allows old account teardown to delete a Project before its populated workspace', async () => { + it('refuses a busy expansion immediately without blocking unrelated writes', async () => { await fixture(async (sql) => { + const [scope] = await sql<{ schema: string }[]>`SELECT current_schema() AS schema` + const writer = postgres(readTestDatabaseUrl(), { max: 1 }) + try { + await writer`SET search_path = ${writer(scope.schema)}` + await writer.unsafe('BEGIN') + await writer`UPDATE workspace SET forked_from_workspace_id = NULL WHERE id = 'personal'` + await expect(applyMigration(sql, migration)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55P03' + ) + await sql.unsafe('ROLLBACK') + await sql`INSERT INTO workspace (id) VALUES ('unblocked-writer')` + } finally { + await writer.unsafe('ROLLBACK') + await writer.end() + } await applyMigration(sql, migration) - - await sql.begin(async (tx) => { - await tx`DELETE FROM project_workspace WHERE project_id = 'private'` - await tx`DELETE FROM project WHERE id = 'private'` - await tx`DELETE FROM workspace WHERE id = 'personal'` - }) - - expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ - { id: 'production', project_id: 'family' }, - { id: 'staging', project_id: 'family' }, - ]) - expect(await sql`SELECT id FROM project`).toEqual([{ id: 'family' }]) - expect(await sql`SELECT workspace_id FROM project_workspace ORDER BY workspace_id`).toEqual([ - { workspace_id: 'production' }, - { workspace_id: 'staging' }, + expect(await sql`SELECT project_id FROM workspace WHERE id = 'unblocked-writer'`).toEqual([ + { project_id: null }, ]) }) }) - it('repairs an interrupted concurrent index build on replay without changing Project assignments', async () => { + it('repairs an interrupted index build without changing assignments', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) + await sql`UPDATE workspace SET project_id = 'family' WHERE id IN ('production', 'staging')` await sql`DROP INDEX CONCURRENTLY workspace_project_id_id_idx` await expect( sql`CREATE UNIQUE INDEX CONCURRENTLY workspace_project_id_id_idx ON workspace(project_id)` ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23505') - expect( - await sql`SELECT indisvalid FROM pg_index - WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` - ).toEqual([{ indisvalid: false }]) - await applyMigration(sql, migration) - await applyMigration(sql, migration) - expect( await sql`SELECT indisvalid, indisunique FROM pg_index - WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` + WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` ).toEqual([{ indisvalid: true, indisunique: false }]) expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ - { id: 'personal', project_id: 'private' }, + { id: 'personal', project_id: null }, { id: 'production', project_id: 'family' }, { id: 'staging', project_id: 'family' }, ]) - await sql.begin(async (tx) => { - await tx`INSERT INTO workspace (id, project_id, forked_from_workspace_id) - VALUES ('new-fork', 'family', 'production')` - }) - expect( - await sql`SELECT count(*)::int AS count FROM workspace WHERE project_id = 'family'` - ).toEqual([{ count: 3 }]) }) }) }) From c5ff743c126c9be4eab6f271726f1852bd443c84 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 13:20:02 -0700 Subject: [PATCH 073/113] fix(projects): run bounded membership backfill before enforcement --- .github/scripts/check-project-rollout.py | 8 +- .github/workflows/migrate.yml | 4 +- .../backfill-repair.integration.ts | 37 +- apps/sim/lib/projects/backfill-repair.ts | 17 + apps/sim/scripts/backfill-projects.ts | 17 +- packages/db/drizzle.config.ts | 1 + packages/db/maintenance/project-backfill.ts | 114 +++-- .../db/maintenance/project-enforcement.ts | 24 ++ .../db/maintenance/project-membership.sql | 268 ++++++++++++ packages/db/maintenance/project-repairs.ts | 32 ++ .../0404_workspace_project_column.sql | 115 +---- .../0405_project_membership_enforcement.sql | 273 +----------- .../db/migrations/meta/0404_snapshot.json | 9 + .../0031_project_membership.ts | 164 +++++++ packages/db/script-migrations/index.ts | 2 + .../db/scripts/project-contract-required.ts | 32 +- .../scripts/project-contract.integration.ts | 360 +++++++++++++--- packages/db/scripts/push.integration.ts | 16 +- .../scripts/reconcile-project-membership.ts | 17 +- .../workspace-project-column.integration.ts | 406 +++--------------- 20 files changed, 1073 insertions(+), 843 deletions(-) create mode 100644 packages/db/maintenance/project-enforcement.ts create mode 100644 packages/db/maintenance/project-membership.sql create mode 100644 packages/db/maintenance/project-repairs.ts create mode 100644 packages/db/script-migrations/0031_project_membership.ts diff --git a/.github/scripts/check-project-rollout.py b/.github/scripts/check-project-rollout.py index ca2b3fcf2eb..6cb93f55f05 100644 --- a/.github/scripts/check-project-rollout.py +++ b/.github/scripts/check-project-rollout.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """Read-only, fail-closed ECS retirement check for the Project column contract migration. -The expected digest acknowledges the fully deployed #8830 column-only readers/writers, -including retirement of pre-8830 servers and relevant old worker jobs. No live or supported -rollback application may still access project_workspace when the contract removes it. +The expected digest acknowledges fully deployed #8830 column-only creation/reassignment +and retirement of pre-8830 servers and relevant old worker jobs. Every live and supported +rollback application must tolerate project_workspace being absent. AWS checks below independently verify ECS retirement, not worker drainage. """ import argparse @@ -43,7 +43,7 @@ def matches_release(execution, digest): def verify(environment, region, digest): if not re.fullmatch(r'sha256:[0-9a-f]{64}', digest): - raise RuntimeError('Set the environment-specific PROJECT_COLUMN_ENFORCEMENT_READY_IMAGE_DIGEST after verifying #8830 column-only readers/writers and pre-8830 server/worker drainage') + raise RuntimeError('Set the environment-specific PROJECT_COLUMN_ENFORCEMENT_READY_IMAGE_DIGEST after verifying #8830 connector-independent readers, column-only writers and pre-8830 server/worker drainage') pipeline = f'sim-{environment}-{region}-app-deployment' execution = latest_execution(region, pipeline) if not matches_release(execution, digest): diff --git a/.github/workflows/migrate.yml b/.github/workflows/migrate.yml index 7a4b0737496..f75cf2fc89b 100644 --- a/.github/workflows/migrate.yml +++ b/.github/workflows/migrate.yml @@ -76,8 +76,8 @@ jobs: role-to-assume: ${{ inputs.environment == 'production' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }} aws-region: ${{ inputs.environment == 'production' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }} - # Acknowledge #8830 only after column-only readers/writers are fully deployed and - # pre-8830 servers and relevant old Trigger.dev runs have drained. SQL removes the connector. + # Acknowledge #8830 only after connector-independent readers and column-only writers deploy and + # pre-8830 servers and relevant old Trigger.dev runs have drained. The registered Project migration backfills, validates, and removes the connector. # The preflight independently checks ECS retirement; it does not inspect worker runs. - name: Require completed column-only rollout before Project enforcement if: steps.project-contract.outputs.required == 'true' diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts index 5016080e1b3..cfeafde1a97 100644 --- a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -92,9 +92,9 @@ beforeAll(async () => { import postgres from 'postgres'; import { drizzle } from 'drizzle-orm/postgres-js'; import { migrate } from 'drizzle-orm/postgres-js/migrator'; - import { runScriptMigrations } from '@sim/db/script-migrations/index'; + import { runScriptMigrations, scriptMigrations } from '@sim/db/script-migrations/index'; const sql = postgres(process.env.MIGRATION_DATABASE_URL,{max:1,onnotice:()=>{}}); - try { await migrate(drizzle(sql),{migrationsFolder:process.env.FIXTURE_MIGRATIONS}); await runScriptMigrations(sql); } + try { await migrate(drizzle(sql),{migrationsFolder:process.env.FIXTURE_MIGRATIONS}); await runScriptMigrations(sql, scriptMigrations.filter((item) => item.name !== '0031_project_membership')); } finally { await sql.end(); } `, ], @@ -152,6 +152,9 @@ describe('Operator archive repair against the full compatible schema', () => { await tx`SELECT pg_advisory_xact_lock(hashtextextended(${key},0))` } await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + expect( + await client`SELECT workspace_id FROM project_backfill_archive_repairs WHERE workspace_id = ${busyId}` + ).toHaveLength(0) expect(JSON.parse(await readFile(artifacts.report, 'utf8'))).toMatchObject({ status: 'incomplete', repairsCompleted: [freeId], @@ -231,10 +234,40 @@ describe('Operator archive repair against the full compatible schema', () => { ).toHaveLength(0) expect(await client`SELECT id FROM workspace WHERE project_id IS NULL`).toHaveLength(1) await expect(run('verify')).rejects.toMatchObject({ code: 2 }) + expect( + await client`SELECT workspace_id FROM project_backfill_archive_repairs WHERE completed_at IS NULL` + ).toEqual([{ workspace_id: 'env' }]) + await expect( + execute( + 'bun', + [ + '--no-env-file', + '-e', + ` + import postgres from 'postgres'; + import { runScriptMigrations, scriptMigrations } from '@sim/db/script-migrations/index'; + const sql = postgres(process.env.MIGRATION_DATABASE_URL, {max:1}); + try { await runScriptMigrations(sql, scriptMigrations.filter((item) => item.name === '0031_project_membership')); } + finally { await sql.end(); } + `, + ], + { + cwd: new URL('../../../../../packages/db/', import.meta.url), + env: environment, + timeout: 15000, + } + ) + ).rejects.toMatchObject({ code: 1 }) + expect(await client`SELECT to_regclass('project_workspace')::text AS connector`).toEqual([ + { connector: 'project_workspace' }, + ]) /** The fixture's missing provider credentials are remediated without making an external request. */ await client`UPDATE webhook SET provider = 'generic'` await expect(run('repair')).rejects.toMatchObject({ code: 2 }) expect(JSON.parse(await readFile(report, 'utf8'))).toMatchObject({ repairsCompleted: ['env'] }) + expect( + await client`SELECT workspace_id FROM project_backfill_archive_repairs WHERE completed_at IS NULL` + ).toHaveLength(0) await run('apply') await run('verify') expect(await client`SELECT archived_at::text FROM project`).toEqual([ diff --git a/apps/sim/lib/projects/backfill-repair.ts b/apps/sim/lib/projects/backfill-repair.ts index 814fa4e14bc..f6d62e6ca87 100644 --- a/apps/sim/lib/projects/backfill-repair.ts +++ b/apps/sim/lib/projects/backfill-repair.ts @@ -3,6 +3,10 @@ import { ProjectBackfillBusy, ProjectBackfillConflict, } from '@sim/db/maintenance/project-backfill' +import { + completeProjectArchiveRepair, + ensureProjectArchiveRepairJournal, +} from '@sim/db/maintenance/project-repairs' import * as schema from '@sim/db/schema' import { and, eq, inArray, isNull, sql } from 'drizzle-orm' import { drizzle } from 'drizzle-orm/postgres-js' @@ -23,6 +27,7 @@ export async function repairArchivedProjectEnvironment( repair: ProjectArchiveRepair, requestId: string ): Promise { + await ensureProjectArchiveRepairJournal(client) const connection = drizzle({ client, schema }) const effects = await connection.transaction(async (tx) => { await tx.execute(sql`SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL @@ -70,6 +75,17 @@ export async function repairArchivedProjectEnvironment( ) .limit(1) if (moved.length) throw new ProjectBackfillConflict('Reviewed workflow moved; rediscover') + const recorded = + await tx.execute(sql`INSERT INTO project_backfill_archive_repairs (workspace_id, repair) + VALUES (${repair.workspaceId}, ${JSON.stringify(repair)}::text::jsonb) + ON CONFLICT (workspace_id) DO UPDATE SET repair = EXCLUDED.repair, completed_at = NULL + WHERE project_backfill_archive_repairs.repair = EXCLUDED.repair + OR project_backfill_archive_repairs.completed_at IS NOT NULL + RETURNING workspace_id`) + if (!recorded.length) + throw new ProjectBackfillConflict( + 'Finish the pending repair with its original manifest before replacing it' + ) await archiveEnvironmentInTransaction( tx, repair.workspaceId, @@ -97,4 +113,5 @@ export async function repairArchivedProjectEnvironment( return result }) await finishEnvironmentArchive(effects, requestId, { strictExternalCleanup: true }) + await completeProjectArchiveRepair(client, repair) } diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index e5fdb31be4e..7a38272dde5 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -32,6 +32,7 @@ const member = z organizationId: text.nullable(), archivedAt: text.nullable(), projectId: text.nullable(), + legacyProjectId: text.nullable(), }) .strict() const manifestSchema = z @@ -127,11 +128,13 @@ async function main() { }, }) if (values.help || !positionals.length) { - process.stdout.write(`Operator-controlled Project preparation; never runs automatically on deployment.\n + process.stdout.write(`Operator-controlled Project inspection and exceptional archive repair. +Routine assignment and enforcement run through db:migrate.\n From apps/sim: bun --no-env-file scripts/backfill-projects.ts [options]\n -plan Read-only discovery; atomically writes a new --manifest PATH (never overwrites).\nrepair Repair only archived environments listed in --manifest; requires --report PATH.\napply Assign reviewed families from --manifest; requires --report PATH.\nverify Validate the database and repair completion; requires --manifest and --report.\nstatus Read --report; does not connect or resume.\n +plan Read-only discovery; atomically writes a new --manifest PATH (never overwrites).\nrepair Repair only archived environments listed in --manifest; requires --report PATH.\napply Optional maintenance: assign reviewed families from --manifest; requires --report PATH.\nverify Validate the database and repair completion; requires --manifest and --report.\nstatus Read --report; does not connect or resume.\n Writes require MIGRATION_DATABASE_URL pointing directly to the primary and --ack-release-drained.\nDeploy #8830 and verify old servers/workers drained first. Keep the reviewed manifest and report\nin durable private job storage. Review conflicts and repairs before repair/apply. Repair requires\nthe app runtime environment (DATABASE_URL must target the same database) for provider cleanup.\n -Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (1–3600), --pause-ms 100 (1–60000).\nA budget limits scheduling. Transactions use 3s statement limits and a 5s total limit on PG17+,\notherwise a 5s idle-transaction limit on PG16. Reuse the same report\nto resume; lost reports can be recreated safely from the same manifest. Changed families need a\nnew plan/report. Complete verify plus deployment evidence is required before #8590 enforcement.\nExit: 0 complete, 2 running/paused/incomplete, 1 failure. Never run db:migrate to launch this tool.\n`) +Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (1–3600), --pause-ms 100 (1–60000).\nA budget limits scheduling. Transactions use 3s statement limits and a 5s total limit on PG17+,\notherwise a 5s idle-transaction limit on PG16. Reuse the same report\nto resume; lost reports can be recreated safely from the same manifest. Changed families need a\nnew plan/report. Resolve conflicts and complete archive/provider cleanup before #8590. +The registered migration backfills remaining assignments, verifies, then enforces.\nExit: 0 complete, 2 running/paused/incomplete, 1 failure. Use db:migrate for routine backfill and enforcement after writer drainage.\n`) return } const [command] = positionals @@ -232,9 +235,11 @@ Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 report.manifestHash !== hash ) throw new Error('Verification artifacts do not match this database and manifest') - counts.pendingCleanup = manifest.repairs.filter( - (repair) => !report.repairsCompleted.includes(repair.workspaceId) - ).length + counts.pendingCleanup = Math.max( + counts.pendingCleanup, + manifest.repairs.filter((repair) => !report.repairsCompleted.includes(repair.workspaceId)) + .length + ) counts.conflicts = manifest.conflicts.length await assertLock() logger.info('Project verification', { databaseId, ...counts }) diff --git a/packages/db/drizzle.config.ts b/packages/db/drizzle.config.ts index cffe391ded6..23aef61ba21 100644 --- a/packages/db/drizzle.config.ts +++ b/packages/db/drizzle.config.ts @@ -18,6 +18,7 @@ export default { /** Runner-owned journals and resumable cleanup cursors must survive a development schema push. */ tablesFilter: [ '!script_migrations', + '!project_backfill_archive_repairs', '!search_embedding_cleanup_progress', '!search_embedding_cleanup_targets', '!search_retirement_*', diff --git a/packages/db/maintenance/project-backfill.ts b/packages/db/maintenance/project-backfill.ts index a70971edc36..1e726e4fa1e 100644 --- a/packages/db/maintenance/project-backfill.ts +++ b/packages/db/maintenance/project-backfill.ts @@ -1,4 +1,5 @@ import { createHash } from 'node:crypto' +import { countPendingProjectArchiveRepairs } from '@sim/db/maintenance/project-repairs' import { createLogger } from '@sim/logger' import { generateId } from '@sim/utils/id' import { compareStrings, truncateAtCodePoint } from '@sim/utils/string' @@ -15,6 +16,7 @@ interface ProjectBackfillWorkspace { organizationId: string | null archivedAt: string | null projectId: string | null + legacyProjectId: string | null } interface ProjectBackfillFamily { @@ -67,6 +69,34 @@ export async function assertProjectBackfillDatabase(sql: Sql, write: boolean): P throw new Error('Project writes require the primary database') } +/** The contract locks workspace before retiring the connector; this read lock spans each query batch. */ +async function hasLegacyMemberships(tx: TransactionSql): Promise { + await tx`LOCK TABLE workspace IN ACCESS SHARE MODE` + const [state] = await tx`SELECT to_regclass('public.project_workspace') IS NOT NULL AS present` + return state.present +} + +function legacyAssignment(tx: TransactionSql, legacy: boolean) { + return legacy + ? tx`CASE WHEN w.project_id IS NULL THEN + (SELECT pw.project_id FROM project_workspace pw WHERE pw.workspace_id = w.id) END` + : tx`NULL::text` +} + +function effectiveProjectId(row: ProjectBackfillWorkspace): string | null { + return row.projectId ?? row.legacyProjectId +} + +function projectEnvironments(tx: TransactionSql, legacy: boolean, projectId: string) { + const columns = tx`SELECT id, archived_at FROM workspace WHERE project_id = ${projectId}` + return legacy + ? tx`${columns} UNION ALL + SELECT w.id, w.archived_at FROM project_workspace pw + JOIN workspace w ON w.id = pw.workspace_id + WHERE pw.project_id = ${projectId} AND w.project_id IS NULL` + : columns +} + /** Bounded discovery materializes only identity/scope metadata, never workflow content. */ export async function discoverProjectBackfill( sql: Sql, @@ -75,11 +105,16 @@ export async function discoverProjectBackfill( const rows: ProjectBackfillWorkspace[] = [] let after = '' for (;;) { - const page = await sql` - SELECT id, forked_from_workspace_id AS "parentId", owner_id AS "ownerId", - organization_id AS "organizationId", archived_at::text AS "archivedAt", project_id AS "projectId" - FROM workspace WHERE id COLLATE "C" > ${after} COLLATE "C" ORDER BY id COLLATE "C" LIMIT 1000 - ` + const page = await sql.begin(async (tx) => { + const legacy = await hasLegacyMemberships(tx) + return tx` + SELECT w.id, w.forked_from_workspace_id AS "parentId", w.owner_id AS "ownerId", + w.organization_id AS "organizationId", w.archived_at::text AS "archivedAt", + w.project_id AS "projectId", ${legacyAssignment(tx, legacy)} AS "legacyProjectId" + FROM workspace w WHERE w.id COLLATE "C" > ${after} COLLATE "C" + ORDER BY w.id COLLATE "C" LIMIT 1000 + ` + }) if (!page.length) break rows.push(...page) if (rows.length > MAX_WORKSPACES) @@ -200,7 +235,7 @@ function validateFamily(family: ProjectBackfillFamily): void { throw new ProjectBackfillConflict( 'Personal family has multiple lifecycle owners; select an owner before rediscovery' ) - if (new Set(family.members.map((row) => row.projectId).filter(Boolean)).size > 1) + if (new Set(family.members.map(effectiveProjectId).filter(Boolean)).size > 1) throw new ProjectBackfillConflict('Fork family spans Projects') } @@ -227,6 +262,7 @@ export async function assignProjectBackfillBatch(sql: Sql, families: ProjectBack THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true)` await tx`SET LOCAL statement_timeout = '3s'` await tx`SET LOCAL lock_timeout = '250ms'` + const legacy = await hasLegacyMemberships(tx) const roots = families.map((f) => f.rootId).sort(compareStrings) const expected = families.flatMap((f) => f.members).sort((a, b) => compareStrings(a.id, b.id)) const ids = expected.map((row) => row.id) @@ -240,18 +276,38 @@ export async function assignProjectBackfillBatch(sql: Sql, families: ProjectBack tx, ids.map((id) => `project-backfill:${id}`) ) - const before = await tx<{ projectId: string }[]>`SELECT project_id AS "projectId" FROM workspace - WHERE id = ANY(${ids}) AND project_id IS NOT NULL GROUP BY project_id ORDER BY project_id COLLATE "C"` + const before = await tx<{ projectId: string }[]>` + SELECT DISTINCT coalesce(w.project_id, ${legacyAssignment(tx, legacy)}) COLLATE "C" AS "projectId" + FROM workspace w WHERE w.id = ANY(${ids}) + AND coalesce(w.project_id, ${legacyAssignment(tx, legacy)}) IS NOT NULL + ORDER BY "projectId" + ` if (before.length) await tryProjectBackfillLocks( tx, before.map((row) => `project:${row.projectId}`) ) + await tx`SELECT id FROM workspace WHERE id = ANY(${ids}) ORDER BY id COLLATE "C" FOR NO KEY UPDATE NOWAIT` + if (legacy) { + await tx`SELECT pw.workspace_id FROM project_workspace pw JOIN workspace w ON w.id = pw.workspace_id + WHERE w.id = ANY(${ids}) AND w.project_id IS NULL + ORDER BY pw.workspace_id COLLATE "C" FOR SHARE OF pw NOWAIT` + } const current = await tx<(ProjectBackfillWorkspace & { name: string })[]>` - SELECT id, name, forked_from_workspace_id AS "parentId", owner_id AS "ownerId", - organization_id AS "organizationId", archived_at::text AS "archivedAt", project_id AS "projectId" - FROM workspace WHERE id = ANY(${ids}) ORDER BY id COLLATE "C" FOR NO KEY UPDATE NOWAIT + SELECT w.id, w.name, w.forked_from_workspace_id AS "parentId", w.owner_id AS "ownerId", + w.organization_id AS "organizationId", w.archived_at::text AS "archivedAt", + w.project_id AS "projectId", ${legacyAssignment(tx, legacy)} AS "legacyProjectId" + FROM workspace w WHERE w.id = ANY(${ids}) ORDER BY w.id COLLATE "C" ` + const lockedProjects = new Set(before.map((row) => row.projectId)) + const newProjects = [...new Set(current.map(effectiveProjectId))] + .filter((id): id is string => id !== null && !lockedProjects.has(id)) + .sort(compareStrings) + if (newProjects.length) + await tryProjectBackfillLocks( + tx, + newProjects.map((id) => `project:${id}`) + ) if (current.length !== expected.length) throw new ProjectBackfillConflict('A reviewed workspace disappeared; rediscover') const byId = new Map(current.map((row) => [row.id, row])) @@ -263,7 +319,10 @@ export async function assignProjectBackfillBatch(sql: Sql, families: ProjectBack actual.ownerId !== row.ownerId || actual.organizationId !== row.organizationId || actual.archivedAt !== row.archivedAt || - (row.projectId !== null && actual.projectId !== row.projectId) + (row.projectId !== null && actual.projectId !== row.projectId) || + (effectiveProjectId(row) !== null && + effectiveProjectId(actual) !== effectiveProjectId(row)) || + (actual.projectId === null && actual.legacyProjectId !== row.legacyProjectId) ) throw new ProjectBackfillConflict( 'Reviewed workspace scope, owner, archive state or assignment changed; rediscover' @@ -294,7 +353,7 @@ export async function assignProjectBackfillBatch(sql: Sql, families: ProjectBack .map((row) => byId.get(row.id)) .filter((row) => row !== undefined) const projectIds = [ - ...new Set(members.map((row) => row.projectId).filter((id): id is string => id !== null)), + ...new Set(members.map(effectiveProjectId).filter((id): id is string => id !== null)), ] if (projectIds.length > 1) throw new ProjectBackfillConflict('Family now spans Projects') const archiveAt = members.every((row) => row.archivedAt !== null) @@ -307,21 +366,24 @@ export async function assignProjectBackfillBatch(sql: Sql, families: ProjectBack if (projectId) { const [existing] = await tx`SELECT owner_id, organization_id, archived_at::text FROM project WHERE id = ${projectId} FOR NO KEY UPDATE NOWAIT` + const familyIds = members.map((row) => row.id) + const [membership] = await tx` + SELECT EXISTS (SELECT 1 FROM (${projectEnvironments(tx, legacy, projectId)}) environments + WHERE archived_at IS NULL) AS active, + EXISTS (SELECT 1 FROM (${projectEnvironments(tx, legacy, projectId)}) environments + WHERE NOT id = ANY(${familyIds})) AS outside + ` + const hasActiveEnvironment = archiveAt === null || membership.active if ( !existing || existing.organization_id !== root.organizationId || - (existing.archived_at === null) !== (archiveAt === null) || + (existing.archived_at === null) !== hasActiveEnvironment || (!root.organizationId && existing.owner_id !== root.ownerId) ) throw new ProjectBackfillConflict( 'Existing Project has incompatible ownership, scope or archive state' ) - if ( - members.some((row) => row.projectId === null) && - ( - await tx`SELECT id FROM workspace WHERE project_id = ${projectId} AND NOT id = ANY(${ids}) LIMIT 1` - ).length - ) + if (members.some((row) => effectiveProjectId(row) === null) && membership.outside) throw new ProjectBackfillConflict( 'Partial assignment references a Project outside the reviewed family' ) @@ -342,12 +404,12 @@ export async function assignProjectBackfillBatch(sql: Sql, families: ProjectBack } if (inserts.length) await tx`INSERT INTO project (id,name,owner_id,organization_id,archived_at) - SELECT id,name,owner_id,organization_id,archived_at::timestamp FROM jsonb_to_recordset(${tx.json(inserts)}::jsonb) + SELECT id,name,owner_id,organization_id,archived_at::timestamp FROM jsonb_to_recordset(${JSON.stringify(inserts)}::text::jsonb) AS p(id text,name text,owner_id text,organization_id text,archived_at text)` let assigned = 0 if (assignments.length) { const result = await tx`UPDATE workspace w SET project_id = p.project_id - FROM jsonb_to_recordset(${tx.json(assignments)}::jsonb) AS p(id text, project_id text) + FROM jsonb_to_recordset(${JSON.stringify(assignments)}::text::jsonb) AS p(id text, project_id text) WHERE w.id = p.id AND w.project_id IS NULL RETURNING w.id` assigned = result.length if (assigned !== assignments.length) @@ -377,12 +439,6 @@ export async function verifyProjectBackfill(sql: Sql): Promise { + const source = await readFile(new URL('./project-membership.sql', import.meta.url), 'utf8') + const connection = await sql.reserve() + try { + for (const statement of source.split('--> statement-breakpoint')) { + if (statement.trim()) await connection.unsafe(statement) + } + } catch (error) { + if (!['25P03', '25P04', 'CONNECTION_CLOSED'].includes(getPostgresErrorCode(error) ?? '')) { + await connection.unsafe('ROLLBACK') + } + throw error + } finally { + await connection + .unsafe("SELECT pg_advisory_unlock(hashtextextended('sim:project-backfill-operator', 0))") + .catch(() => undefined) + connection.release() + } +} diff --git a/packages/db/maintenance/project-membership.sql b/packages/db/maintenance/project-membership.sql new file mode 100644 index 00000000000..f615b1a13d1 --- /dev/null +++ b/packages/db/maintenance/project-membership.sql @@ -0,0 +1,268 @@ +-- Installed by the registered Project migration after bounded backfill and verification. +COMMIT; +--> statement-breakpoint +SET statement_timeout = '60s'; +--> statement-breakpoint +DO $$ BEGIN + IF NOT pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator', 0)) THEN + RAISE EXCEPTION 'Project preparation is still running; finish it before enforcement' USING ERRCODE = '55000'; + END IF; +END $$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION pg_temp.validate_project_membership() RETURNS void LANGUAGE plpgsql AS $$ +BEGIN + IF EXISTS ( + WITH RECURSIVE reachable(id) AS ( + SELECT id FROM workspace WHERE forked_from_workspace_id IS NULL + UNION + SELECT w.id FROM workspace w JOIN reachable r ON w.forked_from_workspace_id = r.id + ) SELECT 1 FROM workspace w LEFT JOIN reachable r ON r.id = w.id WHERE r.id IS NULL + ) THEN + RAISE EXCEPTION 'Project backfill found a fork cycle or missing parent; reconcile before retrying' USING ERRCODE = '55000'; + END IF; + IF EXISTS (SELECT 1 FROM workspace w WHERE w.project_id IS NULL) THEN + RAISE EXCEPTION 'Project preparation is incomplete; rerun the Project script migration before enforcement' USING ERRCODE = '55000'; + END IF; + IF EXISTS ( + SELECT 1 FROM project p LEFT JOIN workspace w ON w.project_id = p.id + GROUP BY p.id HAVING count(w.id) = 0 + OR (p.archived_at IS NULL AND count(w.id) FILTER (WHERE w.archived_at IS NULL) = 0) + OR (p.archived_at IS NOT NULL AND count(w.id) FILTER (WHERE w.archived_at IS NULL) > 0) + ) THEN + RAISE EXCEPTION 'Project enforcement requires nonempty Projects with consistent archive state; reconcile and retry the migration' USING ERRCODE = '55000'; + END IF; + IF EXISTS ( + SELECT 1 FROM workspace w JOIN project p ON p.id = w.project_id + WHERE w.organization_id IS DISTINCT FROM p.organization_id + ) OR EXISTS ( + SELECT 1 FROM workspace w LEFT JOIN workspace parent ON parent.id = w.forked_from_workspace_id + WHERE w.forked_from_workspace_id IS NOT NULL AND parent.project_id IS DISTINCT FROM w.project_id + ) THEN + RAISE EXCEPTION 'Project enforcement requires reconciled organization scope and fork membership' USING ERRCODE = '55000'; + END IF; + IF EXISTS ( + SELECT 1 FROM project p JOIN workspace w ON w.project_id = p.id + JOIN workflow f ON f.workspace_id = w.id + WHERE (p.archived_at IS NOT NULL OR w.archived_at IS NOT NULL) AND f.archived_at IS NULL + ) THEN + RAISE EXCEPTION 'Project enforcement requires archived Projects to have no active workflows' USING ERRCODE = '55000'; + END IF; +END; +$$; +--> statement-breakpoint +SELECT pg_temp.validate_project_membership(); +--> statement-breakpoint +SET lock_timeout = '1s'; +--> statement-breakpoint +SET statement_timeout = '5s'; +--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'workspace'::regclass + AND conname = 'workspace_project_id_project_id_fk') THEN + ALTER TABLE workspace ADD CONSTRAINT workspace_project_id_project_id_fk + FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE RESTRICT NOT VALID; + END IF; +END $$; +--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'workspace'::regclass + AND attname = 'project_id' AND attnotnull) + AND NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'workspace'::regclass + AND conname = 'workspace_project_id_present') THEN + ALTER TABLE workspace ADD CONSTRAINT workspace_project_id_present CHECK (project_id IS NOT NULL) NOT VALID; + END IF; +END $$; +--> statement-breakpoint +SET statement_timeout = '60s'; +--> statement-breakpoint +ALTER TABLE workspace VALIDATE CONSTRAINT workspace_project_id_project_id_fk; +--> statement-breakpoint +DO $$ BEGIN + IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'workspace'::regclass + AND conname = 'workspace_project_id_present') THEN + ALTER TABLE workspace VALIDATE CONSTRAINT workspace_project_id_present; + END IF; +END $$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_contract_assert_project(target_id text) RETURNS void +LANGUAGE plpgsql AS $$ +DECLARE + record project%ROWTYPE; + total bigint; + active bigint; +BEGIN + SELECT * INTO record FROM project WHERE id = target_id; + IF NOT FOUND THEN RETURN; END IF; + SELECT count(*), count(*) FILTER (WHERE w.archived_at IS NULL) + INTO total, active FROM workspace w WHERE w.project_id = target_id; + IF total = 0 OR (record.archived_at IS NULL AND active = 0) + OR (record.archived_at IS NOT NULL AND active > 0) THEN + RAISE EXCEPTION 'Project must retain environments with consistent archive state' + USING ERRCODE = '23514', CONSTRAINT = 'project_environment_lifecycle'; + END IF; + IF EXISTS ( + SELECT 1 FROM workspace w + WHERE w.project_id = target_id AND w.organization_id IS DISTINCT FROM record.organization_id + ) THEN + RAISE EXCEPTION 'Project and environment organization must agree' + USING ERRCODE = '23514', CONSTRAINT = 'project_environment_organization'; + END IF; + IF record.archived_at IS NOT NULL AND EXISTS ( + SELECT 1 FROM workspace w JOIN workflow f ON f.workspace_id = w.id + WHERE w.project_id = target_id AND f.archived_at IS NULL + ) THEN + RAISE EXCEPTION 'Archived Project cannot contain active workflows' + USING ERRCODE = '23514', CONSTRAINT = 'project_workflow_lifecycle'; + END IF; +END; +$$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_contract_assert_workspace(target_id text) RETURNS void +LANGUAGE plpgsql AS $$ +DECLARE + parent_id text; + member_project_id text; + parent_project_id text; +BEGIN + SELECT forked_from_workspace_id, project_id INTO parent_id, member_project_id FROM workspace WHERE id = target_id; + IF NOT FOUND THEN RETURN; END IF; + IF parent_id IS NOT NULL THEN + SELECT project_id INTO parent_project_id FROM workspace WHERE id = parent_id; + IF parent_project_id IS DISTINCT FROM member_project_id THEN + RAISE EXCEPTION 'Connected fork environments must belong to the same Project' + USING ERRCODE = '23514', CONSTRAINT = 'workspace_fork_project'; + END IF; + END IF; + IF EXISTS ( + SELECT 1 FROM workspace child + WHERE child.forked_from_workspace_id = target_id AND child.project_id IS DISTINCT FROM member_project_id + ) THEN + RAISE EXCEPTION 'Connected fork environments must belong to the same Project' + USING ERRCODE = '23514', CONSTRAINT = 'workspace_fork_project'; + END IF; +END; +$$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_contract_lock_projects(target_ids text[]) RETURNS void +LANGUAGE plpgsql AS $$ +DECLARE + target_id text; +BEGIN + FOR target_id IN SELECT DISTINCT id FROM unnest(target_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP + -- BEFORE row triggers already hold row locks; waiting here could invert the application lock order. + IF NOT pg_try_advisory_xact_lock(hashtextextended('project:' || target_id, 0)) THEN + RAISE EXCEPTION 'Project is changing; retry the operation' USING ERRCODE = '55P03'; + END IF; + END LOOP; +END; +$$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_contract_before_write() RETURNS trigger +LANGUAGE plpgsql AS $$ +DECLARE + previous_id text; + next_id text; +BEGIN + IF TG_TABLE_NAME = 'project' THEN + IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; + ELSE + IF TG_OP <> 'INSERT' THEN previous_id := OLD.project_id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.project_id; END IF; + END IF; + PERFORM project_contract_lock_projects(ARRAY[previous_id, next_id]); + IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; + RETURN NEW; +END; +$$; +--> statement-breakpoint +CREATE OR REPLACE FUNCTION project_contract_after_write() RETURNS trigger +LANGUAGE plpgsql AS $$ +DECLARE + previous_id text; + next_id text; + target_ids text[]; + workspace_ids text[]; + target_id text; +BEGIN + IF TG_TABLE_NAME = 'project' THEN + -- Only the final row version needs a full scan; earlier deferred events describe superseded states. + IF TG_OP <> 'DELETE' AND EXISTS (SELECT 1 FROM project WHERE id = NEW.id AND ctid = NEW.ctid) THEN + PERFORM project_contract_assert_project(NEW.id); + END IF; + RETURN NULL; + END IF; + IF TG_OP <> 'INSERT' THEN previous_id := OLD.project_id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.project_id; END IF; + target_ids := ARRAY[previous_id, next_id]; + previous_id := NULL; + next_id := NULL; + IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; + IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; + workspace_ids := ARRAY[previous_id, next_id]; + FOR target_id IN SELECT DISTINCT id FROM unnest(target_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP + -- Touch after the mutation: immediate constraints must see it too, and repeatable-read must detect stale snapshots. + UPDATE project SET updated_at = updated_at WHERE id = target_id; + END LOOP; + FOR target_id IN SELECT DISTINCT id FROM unnest(workspace_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP + PERFORM project_contract_assert_workspace(target_id); + END LOOP; + RETURN NULL; +END; +$$; +--> statement-breakpoint +BEGIN; +--> statement-breakpoint +-- PG17 caps the transaction; PG16 caps idle time, with each statement bounded below. +SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL + THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true); +--> statement-breakpoint +SET LOCAL lock_timeout = '1s'; +--> statement-breakpoint +SET LOCAL statement_timeout = '5s'; +--> statement-breakpoint +-- Keep enforcement and bridge retirement atomic; data scans run outside this transaction. +LOCK TABLE workspace, project IN ACCESS EXCLUSIVE MODE NOWAIT; +--> statement-breakpoint +DO $$ BEGIN + IF to_regclass('project_workspace') IS NOT NULL THEN + LOCK TABLE project_workspace IN ACCESS EXCLUSIVE MODE NOWAIT; + END IF; +END $$; +--> statement-breakpoint +-- migration-safe: contract of #8830, gated on its column-only release being fully deployed and all pre-8830 servers/workers having drained; the validated check proves existing rows. +ALTER TABLE workspace ALTER COLUMN project_id SET NOT NULL; +-- migration-safe: the required column now enforces the validated helper check's invariant. +ALTER TABLE workspace DROP CONSTRAINT IF EXISTS workspace_project_id_present; +--> statement-breakpoint +DROP TRIGGER IF EXISTS project_contract_lock ON project; +CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OR DELETE ON project +FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); +DROP TRIGGER IF EXISTS project_contract_check ON project; +CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OR DELETE ON project +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); +--> statement-breakpoint +DROP TRIGGER IF EXISTS project_contract_lock ON workspace; +CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OF id, project_id, archived_at, organization_id, forked_from_workspace_id OR DELETE ON workspace +FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); +DROP TRIGGER IF EXISTS project_contract_check ON workspace; +CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OF id, project_id, archived_at, organization_id, forked_from_workspace_id OR DELETE ON workspace +DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); +--> statement-breakpoint +DROP TRIGGER IF EXISTS workspace_sync_project_membership ON workspace; +-- migration-safe: contract of #8830, gated on its column-only release being fully deployed and all pre-8830 servers/workers having drained; no supported application reader or writer then needs this connector. +DROP TABLE IF EXISTS project_workspace; +DROP FUNCTION IF EXISTS workspace_sync_project_membership_fn(); +DROP FUNCTION IF EXISTS project_workspace_sync_column_fn(); +--> statement-breakpoint +COMMIT; +--> statement-breakpoint +-- Membership triggers and compatible workspace-guarded writers protect new writes during validation. +SELECT pg_temp.validate_project_membership(); +--> statement-breakpoint +DROP FUNCTION pg_temp.validate_project_membership(); +--> statement-breakpoint +SET statement_timeout = 0; +--> statement-breakpoint +SET lock_timeout = '5s'; + diff --git a/packages/db/maintenance/project-repairs.ts b/packages/db/maintenance/project-repairs.ts new file mode 100644 index 00000000000..60897d9f5c4 --- /dev/null +++ b/packages/db/maintenance/project-repairs.ts @@ -0,0 +1,32 @@ +import type { ProjectArchiveRepair } from '@sim/db/maintenance/project-backfill' +import type { Sql } from 'postgres' + +/** Runner-managed recovery state, like script_migrations; never an application ownership table. */ +export async function ensureProjectArchiveRepairJournal(sql: Sql): Promise { + await sql`CREATE TABLE IF NOT EXISTS project_backfill_archive_repairs ( + workspace_id text PRIMARY KEY, + repair jsonb NOT NULL, + completed_at timestamptz + )` +} + +/** Written only after all external cleanup succeeds, so deployment cannot overlook a partial repair. */ +export async function completeProjectArchiveRepair( + sql: Sql, + repair: ProjectArchiveRepair +): Promise { + const rows = await sql`UPDATE project_backfill_archive_repairs SET completed_at = now() + WHERE workspace_id = ${repair.workspaceId} AND repair = ${JSON.stringify(repair)}::text::jsonb + RETURNING workspace_id` + if (!rows.length) throw new Error('Archive repair changed before cleanup completed') +} + +/** Read-only, including on databases that have never needed archive repair. */ +export async function countPendingProjectArchiveRepairs(sql: Sql): Promise { + const [state] = + await sql`SELECT to_regclass('public.project_backfill_archive_repairs') IS NOT NULL AS present` + if (!state.present) return 0 + const [row] = + await sql`SELECT count(*)::int AS count FROM project_backfill_archive_repairs WHERE completed_at IS NULL` + return row.count +} diff --git a/packages/db/migrations/0404_workspace_project_column.sql b/packages/db/migrations/0404_workspace_project_column.sql index 563c917e196..a7d90ceec3a 100644 --- a/packages/db/migrations/0404_workspace_project_column.sql +++ b/packages/db/migrations/0404_workspace_project_column.sql @@ -4,50 +4,18 @@ BEGIN; SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true); SET LOCAL statement_timeout = '5s'; --- Install both directions together, without queuing behind a live writer. LOCK TABLE workspace IN ACCESS EXCLUSIVE MODE NOWAIT; -LOCK TABLE project_workspace IN SHARE ROW EXCLUSIVE MODE NOWAIT; +LOCK TABLE project IN SHARE ROW EXCLUSIVE MODE NOWAIT; ALTER TABLE "workspace" ADD COLUMN IF NOT EXISTS "project_id" text; - -CREATE OR REPLACE FUNCTION workspace_sync_project_membership_fn() RETURNS trigger LANGUAGE plpgsql AS $$ -BEGIN - IF TG_OP = 'UPDATE' AND OLD.project_id IS NOT DISTINCT FROM NEW.project_id THEN - RETURN NEW; - END IF; - IF NEW.project_id IS NULL THEN - DELETE FROM project_workspace WHERE workspace_id = NEW.id; - ELSE - INSERT INTO project_workspace (project_id, workspace_id) VALUES (NEW.project_id, NEW.id) - ON CONFLICT (workspace_id) DO UPDATE SET project_id = EXCLUDED.project_id - WHERE project_workspace.project_id IS DISTINCT FROM EXCLUDED.project_id; - END IF; - RETURN NEW; -END; -$$; -CREATE OR REPLACE TRIGGER workspace_sync_project_membership - AFTER INSERT OR UPDATE OF project_id ON workspace - FOR EACH ROW EXECUTE FUNCTION workspace_sync_project_membership_fn(); - -CREATE OR REPLACE FUNCTION project_workspace_sync_column_fn() RETURNS trigger LANGUAGE plpgsql AS $$ -BEGIN - IF TG_OP = 'DELETE' THEN - UPDATE workspace SET project_id = NULL WHERE id = OLD.workspace_id AND project_id IS NOT NULL; - RETURN OLD; - END IF; - IF TG_OP = 'UPDATE' AND OLD.workspace_id IS DISTINCT FROM NEW.workspace_id THEN - RAISE EXCEPTION 'Project membership workspace identity cannot change' USING ERRCODE = '55000'; +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint + WHERE conrelid = 'workspace'::regclass AND conname = 'workspace_project_id_project_id_fk') THEN + ALTER TABLE "workspace" ADD CONSTRAINT "workspace_project_id_project_id_fk" + FOREIGN KEY ("project_id") REFERENCES "project"("id") ON DELETE RESTRICT NOT VALID; END IF; - UPDATE workspace SET project_id = NEW.project_id - WHERE id = NEW.workspace_id AND project_id IS DISTINCT FROM NEW.project_id; - RETURN NEW; -END; -$$; -CREATE OR REPLACE TRIGGER project_workspace_sync_column - AFTER INSERT OR UPDATE OR DELETE ON project_workspace - FOR EACH ROW EXECUTE FUNCTION project_workspace_sync_column_fn(); +END $$; COMMIT; --> statement-breakpoint --- This covers index construction and the whole copy CALL; bounded copy batches commit independently. SET statement_timeout = '15min'; --> statement-breakpoint SET lock_timeout = 0; @@ -57,75 +25,6 @@ DROP INDEX CONCURRENTLY IF EXISTS "workspace_project_id_id_idx"; --> statement-breakpoint CREATE INDEX CONCURRENTLY IF NOT EXISTS "workspace_project_id_id_idx" ON "workspace" USING btree ("project_id","id"); --> statement-breakpoint -SET lock_timeout = '1s'; ---> statement-breakpoint -CREATE OR REPLACE PROCEDURE pg_temp.copy_workspace_projects() LANGUAGE plpgsql AS $$ -DECLARE - previous_id text; - batch_ids text[]; - attempts integer := 0; - copied integer := 0; - changed integer; - retry boolean; -BEGIN - LOOP - SELECT array_agg(id ORDER BY id COLLATE "C") INTO batch_ids FROM ( - SELECT w.id FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE (previous_id IS NULL OR w.id COLLATE "C" > previous_id COLLATE "C") - AND w.project_id IS DISTINCT FROM pw.project_id - ORDER BY w.id COLLATE "C" LIMIT 100 - ) batch; - EXIT WHEN batch_ids IS NULL; - PERFORM set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL - THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true); - retry := false; - BEGIN - PERFORM id FROM workspace WHERE id = ANY(batch_ids) ORDER BY id COLLATE "C" - FOR NO KEY UPDATE NOWAIT; - -- Old writers can hold connector rows first: release the entire batch instead of waiting. - PERFORM workspace_id FROM project_workspace WHERE workspace_id = ANY(batch_ids) - ORDER BY workspace_id COLLATE "C" FOR UPDATE NOWAIT; - -- Read again after locking so a concurrent detach cannot be overwritten by discovery's snapshot. - UPDATE workspace w SET project_id = pw.project_id FROM project_workspace pw - WHERE w.id = ANY(batch_ids) AND pw.workspace_id = w.id - AND w.project_id IS DISTINCT FROM pw.project_id; - GET DIAGNOSTICS changed = ROW_COUNT; - copied := copied + changed; - EXCEPTION WHEN lock_not_available OR deadlock_detected OR serialization_failure THEN - retry := true; - END; - COMMIT; - IF retry THEN - attempts := attempts + 1; - IF attempts >= 20 THEN - RAISE EXCEPTION 'Project column copy remains busy; safe to retry migration' - USING ERRCODE = '55P03', DETAIL = batch_ids[1]; - END IF; - PERFORM pg_sleep(0.05 + random() * 0.15); - COMMIT; - ELSE - attempts := 0; - previous_id := batch_ids[cardinality(batch_ids)]; - END IF; - END LOOP; - RAISE NOTICE 'Project column copy complete: % environments copied', copied; -END; -$$; ---> statement-breakpoint -CALL pg_temp.copy_workspace_projects(); ---> statement-breakpoint -DROP PROCEDURE pg_temp.copy_workspace_projects(); ---> statement-breakpoint -DO $$ BEGIN - IF EXISTS ( - SELECT 1 FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE w.project_id IS DISTINCT FROM pw.project_id - ) THEN - RAISE EXCEPTION 'Project column and connector assignments disagree; reconcile before retrying' - USING ERRCODE = '55000'; - END IF; -END $$; ---> statement-breakpoint SET lock_timeout = '5s'; --> statement-breakpoint SET statement_timeout = 0; diff --git a/packages/db/migrations/0405_project_membership_enforcement.sql b/packages/db/migrations/0405_project_membership_enforcement.sql index d1ba7301305..d0052e28e34 100644 --- a/packages/db/migrations/0405_project_membership_enforcement.sql +++ b/packages/db/migrations/0405_project_membership_enforcement.sql @@ -1,270 +1,3 @@ --- Preparation and assignments are operator-controlled; this migration only validates and enforces. -COMMIT; ---> statement-breakpoint -SET statement_timeout = '60s'; ---> statement-breakpoint -DO $$ BEGIN - IF NOT pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator', 0)) THEN - RAISE EXCEPTION 'Project preparation is still running; finish it before enforcement' USING ERRCODE = '55000'; - END IF; -END $$; ---> statement-breakpoint -CREATE OR REPLACE FUNCTION pg_temp.validate_project_membership() RETURNS void LANGUAGE plpgsql AS $$ -BEGIN - IF EXISTS ( - WITH RECURSIVE reachable(id) AS ( - SELECT id FROM workspace WHERE forked_from_workspace_id IS NULL - UNION - SELECT w.id FROM workspace w JOIN reachable r ON w.forked_from_workspace_id = r.id - ) SELECT 1 FROM workspace w LEFT JOIN reachable r ON r.id = w.id WHERE r.id IS NULL - ) THEN - RAISE EXCEPTION 'Project backfill found a fork cycle or missing parent; reconcile before retrying' USING ERRCODE = '55000'; - END IF; - IF EXISTS (SELECT 1 FROM workspace w WHERE w.project_id IS NULL) THEN - RAISE EXCEPTION 'Project preparation is incomplete; run backfill-projects.ts plan/repair/apply/verify before enforcement' USING ERRCODE = '55000'; - END IF; - IF EXISTS ( - SELECT 1 FROM project p LEFT JOIN workspace w ON w.project_id = p.id - GROUP BY p.id HAVING count(w.id) = 0 - OR (p.archived_at IS NULL AND count(w.id) FILTER (WHERE w.archived_at IS NULL) = 0) - OR (p.archived_at IS NOT NULL AND count(w.id) FILTER (WHERE w.archived_at IS NULL) > 0) - ) THEN - RAISE EXCEPTION 'Project enforcement requires nonempty Projects with consistent archive state; reconcile and retry the migration' USING ERRCODE = '55000'; - END IF; - IF EXISTS ( - SELECT 1 FROM workspace w JOIN project p ON p.id = w.project_id - WHERE w.organization_id IS DISTINCT FROM p.organization_id - ) OR EXISTS ( - SELECT 1 FROM workspace w LEFT JOIN workspace parent ON parent.id = w.forked_from_workspace_id - WHERE w.forked_from_workspace_id IS NOT NULL AND parent.project_id IS DISTINCT FROM w.project_id - ) THEN - RAISE EXCEPTION 'Project enforcement requires reconciled organization scope and fork membership' USING ERRCODE = '55000'; - END IF; - IF EXISTS ( - SELECT 1 FROM project p JOIN workspace w ON w.project_id = p.id - JOIN workflow f ON f.workspace_id = w.id - WHERE (p.archived_at IS NOT NULL OR w.archived_at IS NOT NULL) AND f.archived_at IS NULL - ) THEN - RAISE EXCEPTION 'Project enforcement requires archived Projects to have no active workflows' USING ERRCODE = '55000'; - END IF; -END; -$$; ---> statement-breakpoint -SELECT pg_temp.validate_project_membership(); ---> statement-breakpoint -SET lock_timeout = '1s'; ---> statement-breakpoint -SET statement_timeout = '5s'; ---> statement-breakpoint -DO $$ BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'workspace'::regclass - AND conname = 'workspace_project_id_project_id_fk') THEN - ALTER TABLE workspace ADD CONSTRAINT workspace_project_id_project_id_fk - FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE RESTRICT NOT VALID; - END IF; -END $$; ---> statement-breakpoint -DO $$ BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'workspace'::regclass - AND attname = 'project_id' AND attnotnull) - AND NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'workspace'::regclass - AND conname = 'workspace_project_id_present') THEN - ALTER TABLE workspace ADD CONSTRAINT workspace_project_id_present CHECK (project_id IS NOT NULL) NOT VALID; - END IF; -END $$; ---> statement-breakpoint -SET statement_timeout = '60s'; ---> statement-breakpoint -ALTER TABLE workspace VALIDATE CONSTRAINT workspace_project_id_project_id_fk; ---> statement-breakpoint -DO $$ BEGIN - IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'workspace'::regclass - AND conname = 'workspace_project_id_present') THEN - ALTER TABLE workspace VALIDATE CONSTRAINT workspace_project_id_present; - END IF; -END $$; ---> statement-breakpoint -CREATE OR REPLACE FUNCTION project_contract_assert_project(target_id text) RETURNS void -LANGUAGE plpgsql AS $$ -DECLARE - record project%ROWTYPE; - total bigint; - active bigint; -BEGIN - SELECT * INTO record FROM project WHERE id = target_id; - IF NOT FOUND THEN RETURN; END IF; - SELECT count(*), count(*) FILTER (WHERE w.archived_at IS NULL) - INTO total, active FROM workspace w WHERE w.project_id = target_id; - IF total = 0 OR (record.archived_at IS NULL AND active = 0) - OR (record.archived_at IS NOT NULL AND active > 0) THEN - RAISE EXCEPTION 'Project must retain environments with consistent archive state' - USING ERRCODE = '23514', CONSTRAINT = 'project_environment_lifecycle'; - END IF; - IF EXISTS ( - SELECT 1 FROM workspace w - WHERE w.project_id = target_id AND w.organization_id IS DISTINCT FROM record.organization_id - ) THEN - RAISE EXCEPTION 'Project and environment organization must agree' - USING ERRCODE = '23514', CONSTRAINT = 'project_environment_organization'; - END IF; - IF record.archived_at IS NOT NULL AND EXISTS ( - SELECT 1 FROM workspace w JOIN workflow f ON f.workspace_id = w.id - WHERE w.project_id = target_id AND f.archived_at IS NULL - ) THEN - RAISE EXCEPTION 'Archived Project cannot contain active workflows' - USING ERRCODE = '23514', CONSTRAINT = 'project_workflow_lifecycle'; - END IF; -END; -$$; ---> statement-breakpoint -CREATE OR REPLACE FUNCTION project_contract_assert_workspace(target_id text) RETURNS void -LANGUAGE plpgsql AS $$ -DECLARE - parent_id text; - member_project_id text; - parent_project_id text; -BEGIN - SELECT forked_from_workspace_id, project_id INTO parent_id, member_project_id FROM workspace WHERE id = target_id; - IF NOT FOUND THEN RETURN; END IF; - IF parent_id IS NOT NULL THEN - SELECT project_id INTO parent_project_id FROM workspace WHERE id = parent_id; - IF parent_project_id IS DISTINCT FROM member_project_id THEN - RAISE EXCEPTION 'Connected fork environments must belong to the same Project' - USING ERRCODE = '23514', CONSTRAINT = 'workspace_fork_project'; - END IF; - END IF; - IF EXISTS ( - SELECT 1 FROM workspace child - WHERE child.forked_from_workspace_id = target_id AND child.project_id IS DISTINCT FROM member_project_id - ) THEN - RAISE EXCEPTION 'Connected fork environments must belong to the same Project' - USING ERRCODE = '23514', CONSTRAINT = 'workspace_fork_project'; - END IF; -END; -$$; ---> statement-breakpoint -CREATE OR REPLACE FUNCTION project_contract_lock_projects(target_ids text[]) RETURNS void -LANGUAGE plpgsql AS $$ -DECLARE - target_id text; -BEGIN - FOR target_id IN SELECT DISTINCT id FROM unnest(target_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP - -- BEFORE row triggers already hold row locks; waiting here could invert the application lock order. - IF NOT pg_try_advisory_xact_lock(hashtextextended('project:' || target_id, 0)) THEN - RAISE EXCEPTION 'Project is changing; retry the operation' USING ERRCODE = '55P03'; - END IF; - END LOOP; -END; -$$; ---> statement-breakpoint -CREATE OR REPLACE FUNCTION project_contract_before_write() RETURNS trigger -LANGUAGE plpgsql AS $$ -DECLARE - previous_id text; - next_id text; -BEGIN - IF TG_TABLE_NAME = 'project' THEN - IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; - IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; - ELSE - IF TG_OP <> 'INSERT' THEN previous_id := OLD.project_id; END IF; - IF TG_OP <> 'DELETE' THEN next_id := NEW.project_id; END IF; - END IF; - PERFORM project_contract_lock_projects(ARRAY[previous_id, next_id]); - IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; - RETURN NEW; -END; -$$; ---> statement-breakpoint -CREATE OR REPLACE FUNCTION project_contract_after_write() RETURNS trigger -LANGUAGE plpgsql AS $$ -DECLARE - previous_id text; - next_id text; - target_ids text[]; - workspace_ids text[]; - target_id text; -BEGIN - IF TG_TABLE_NAME = 'project' THEN - -- Only the final row version needs a full scan; earlier deferred events describe superseded states. - IF TG_OP <> 'DELETE' AND EXISTS (SELECT 1 FROM project WHERE id = NEW.id AND ctid = NEW.ctid) THEN - PERFORM project_contract_assert_project(NEW.id); - END IF; - RETURN NULL; - END IF; - IF TG_OP <> 'INSERT' THEN previous_id := OLD.project_id; END IF; - IF TG_OP <> 'DELETE' THEN next_id := NEW.project_id; END IF; - target_ids := ARRAY[previous_id, next_id]; - previous_id := NULL; - next_id := NULL; - IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; - IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; - workspace_ids := ARRAY[previous_id, next_id]; - FOR target_id IN SELECT DISTINCT id FROM unnest(target_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP - -- Touch after the mutation: immediate constraints must see it too, and repeatable-read must detect stale snapshots. - UPDATE project SET updated_at = updated_at WHERE id = target_id; - END LOOP; - FOR target_id IN SELECT DISTINCT id FROM unnest(workspace_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP - PERFORM project_contract_assert_workspace(target_id); - END LOOP; - RETURN NULL; -END; -$$; ---> statement-breakpoint -BEGIN; ---> statement-breakpoint --- PG17 caps the transaction; PG16 caps idle time, with each statement bounded below. -SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL - THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true); ---> statement-breakpoint -SET LOCAL lock_timeout = '1s'; ---> statement-breakpoint -SET LOCAL statement_timeout = '5s'; ---> statement-breakpoint --- Keep enforcement and bridge retirement atomic; data scans run outside this transaction. -LOCK TABLE workspace, project IN ACCESS EXCLUSIVE MODE NOWAIT; ---> statement-breakpoint -DO $$ BEGIN - IF to_regclass('project_workspace') IS NOT NULL THEN - LOCK TABLE project_workspace IN ACCESS EXCLUSIVE MODE NOWAIT; - END IF; -END $$; ---> statement-breakpoint --- migration-safe: contract of #8830, gated on its column-only release being fully deployed and all pre-8830 servers/workers having drained; the validated check proves existing rows. -ALTER TABLE workspace ALTER COLUMN project_id SET NOT NULL; --- migration-safe: the required column now enforces the validated helper check's invariant. -ALTER TABLE workspace DROP CONSTRAINT IF EXISTS workspace_project_id_present; ---> statement-breakpoint -DROP TRIGGER IF EXISTS project_contract_lock ON project; -CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OR DELETE ON project -FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); -DROP TRIGGER IF EXISTS project_contract_check ON project; -CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OR DELETE ON project -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); ---> statement-breakpoint -DROP TRIGGER IF EXISTS project_contract_lock ON workspace; -CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OF id, project_id, archived_at, organization_id, forked_from_workspace_id OR DELETE ON workspace -FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); -DROP TRIGGER IF EXISTS project_contract_check ON workspace; -CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OF id, project_id, archived_at, organization_id, forked_from_workspace_id OR DELETE ON workspace -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); ---> statement-breakpoint -DROP TRIGGER IF EXISTS workspace_sync_project_membership ON workspace; --- migration-safe: contract of #8830, gated on its column-only release being fully deployed and all pre-8830 servers/workers having drained; no supported application reader or writer then needs this connector. -DROP TABLE IF EXISTS project_workspace; -DROP FUNCTION IF EXISTS workspace_sync_project_membership_fn(); -DROP FUNCTION IF EXISTS project_workspace_sync_column_fn(); ---> statement-breakpoint -COMMIT; ---> statement-breakpoint --- Membership triggers and compatible workspace-guarded writers protect new writes during validation. -SELECT pg_temp.validate_project_membership(); ---> statement-breakpoint -DROP FUNCTION pg_temp.validate_project_membership(); ---> statement-breakpoint -SET statement_timeout = 0; ---> statement-breakpoint -SET lock_timeout = '5s'; - ---> statement-breakpoint -SELECT pg_advisory_unlock(hashtextextended('sim:project-backfill-operator', 0)); +-- The registered 0031_project_membership script backfills and verifies before installing +-- the final constraints and retiring the connector. SQL migrations run before scripts. +SELECT 1; diff --git a/packages/db/migrations/meta/0404_snapshot.json b/packages/db/migrations/meta/0404_snapshot.json index e1a1bf36593..264a1375ce6 100644 --- a/packages/db/migrations/meta/0404_snapshot.json +++ b/packages/db/migrations/meta/0404_snapshot.json @@ -27171,6 +27171,15 @@ "onDelete": "set null", "onUpdate": "no action" }, + "workspace_project_id_project_id_fk": { + "name": "workspace_project_id_project_id_fk", + "tableFrom": "workspace", + "tableTo": "project", + "columnsFrom": ["project_id"], + "columnsTo": ["id"], + "onDelete": "restrict", + "onUpdate": "no action" + }, "workspace_billed_account_user_id_user_id_fk": { "name": "workspace_billed_account_user_id_user_id_fk", "tableFrom": "workspace", diff --git a/packages/db/script-migrations/0031_project_membership.ts b/packages/db/script-migrations/0031_project_membership.ts new file mode 100644 index 00000000000..421cee46517 --- /dev/null +++ b/packages/db/script-migrations/0031_project_membership.ts @@ -0,0 +1,164 @@ +import { + assertProjectBackfillDatabase, + assignProjectBackfillBatch, + discoverProjectBackfill, + ProjectBackfillBusy, + ProjectBackfillConflict, + verifyProjectBackfill, +} from '@sim/db/maintenance/project-backfill' +import { enforceProjectMembership } from '@sim/db/maintenance/project-enforcement' +import { countPendingProjectArchiveRepairs } from '@sim/db/maintenance/project-repairs' +import type { ScriptMigration } from '@sim/db/script-migrations/types' +import { createLogger } from '@sim/logger' +import { describeError, getTransientDatabaseFailure } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { backoffWithJitter } from '@sim/utils/retry' + +const logger = createLogger('ProjectMembershipMigration') +function retryable(error: unknown): boolean { + const failure = getTransientDatabaseFailure(error) + return failure === 'capacity' || failure === 'conflict' +} +const MAX_DURATION_MS = 20 * 60_000 + +/** Commits bounded assignments before validation and contraction; retry discovers only remaining work. */ +export const projectMembershipMigration: ScriptMigration = { + name: '0031_project_membership', + async up(sql) { + if (sql.options.max !== 1) + throw new Error('Project migration requires the runner single-session connection') + await sql.unsafe("SET statement_timeout = '60s'") + await sql.unsafe("SET lock_timeout = '250ms'") + const [lock] = await sql`SELECT pg_backend_pid() AS pid, + pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator', 0)) AS acquired` + if (!lock.acquired) + throw new Error('Project preparation is still running; retry after it finishes') + async function assertSession() { + const [state] = await sql`SELECT pg_backend_pid() AS pid, EXISTS ( + SELECT 1 FROM pg_locks WHERE pid = pg_backend_pid() AND locktype = 'advisory' AND granted + AND classid = ((hashtextextended('sim:project-backfill-operator',0) >> 32) & 4294967295)::oid + AND objid = (hashtextextended('sim:project-backfill-operator',0) & 4294967295)::oid + AND objsubid = 1) AS held` + if (state.pid !== lock.pid || !state.held) + throw new Error('Project migration session was lost; rerun to resume') + } + try { + await assertProjectBackfillDatabase(sql, true) + if (await countPendingProjectArchiveRepairs(sql)) { + throw new Error( + 'Project archive cleanup is incomplete; resume repair with the original manifest before retrying' + ) + } + const manifest = await discoverProjectBackfill(sql, 'migration-runner') + if (manifest.conflicts.length || manifest.repairs.length) { + logger.error('Project preparation requires operator remediation', { + conflicts: manifest.conflicts.slice(0, 50), + conflictCount: manifest.conflicts.length, + archiveRepairs: manifest.repairs.length, + }) + throw new ProjectBackfillConflict( + 'Resolve Project conflicts and archive/provider repairs with backfill-projects.ts plan/repair before retrying' + ) + } + const pending = manifest.families.map((family) => ({ family, attempts: 0 })) + const failed: string[] = [] + const started = performance.now() + let batchSize = 50 + let assigned = 0 + let batches = 0 + for (let position = 0; position < pending.length; ) { + if (performance.now() - started > MAX_DURATION_MS || batches >= 10_000) { + throw new Error( + 'Project backfill scheduling budget exhausted; rerun to resume committed progress' + ) + } + await assertSession() + const items = [pending[position++]] + if (items[0].family.members.length === 1 && items[0].attempts === 0) { + while ( + items.length < batchSize && + position < pending.length && + pending[position].family.members.length === 1 && + pending[position].attempts === 0 + ) { + items.push(pending[position++]) + } + } + const batchStarted = performance.now() + batches++ + try { + const result = await assignProjectBackfillBatch( + sql, + items.map((item) => item.family) + ) + assigned += result.assigned + const elapsedMs = performance.now() - batchStarted + if (elapsedMs > 250) batchSize = Math.max(1, Math.floor(batchSize / 2)) + logger.info('Project migration batch committed', { + ...result, + assigned, + batches, + elapsedMs, + batchSize, + }) + } catch (error) { + if ( + !(error instanceof ProjectBackfillBusy) && + !(error instanceof ProjectBackfillConflict) && + !retryable(error) + ) + throw error + await assertSession() + for (const item of items) { + if (!(error instanceof ProjectBackfillConflict) && ++item.attempts < 3) + pending.push(item) + else failed.push(item.family.rootId) + } + logger.warn('Project migration batch deferred', { + roots: items.map((item) => item.family.rootId), + error: describeError(error), + }) + await sleep(backoffWithJitter(items[0].attempts, null, { baseMs: 250, maxMs: 1000 })) + } + await sleep(100) + } + if (failed.length) { + logger.error('Project families still need retry or reconciliation', { + count: failed.length, + roots: failed.slice(0, 50), + }) + throw new ProjectBackfillConflict( + 'Project backfill incomplete; resolve contention or changed families and rerun' + ) + } + await assertSession() + const counts = await verifyProjectBackfill(sql) + logger.info('Project migration verification', counts) + if (Object.values(counts).some((count) => count !== 0)) { + throw new ProjectBackfillConflict( + 'Project validation failed; reconcile the reported invariants before enforcement' + ) + } + for (let attempt = 0; ; attempt++) { + await assertSession() + try { + await enforceProjectMembership(sql) + break + } catch (error) { + if (attempt >= 2 || !retryable(error)) throw error + logger.warn('Project enforcement deferred by contention', { + error: describeError(error), + attempt, + }) + await sleep(backoffWithJitter(attempt, null, { baseMs: 250, maxMs: 1000 })) + } + } + await assertSession() + logger.info('Project backfill and enforcement complete', { assigned, batches }) + } finally { + await sql`SELECT pg_advisory_unlock(hashtextextended('sim:project-backfill-operator', 0))` + await sql.unsafe('SET statement_timeout = 0') + await sql.unsafe('SET lock_timeout = 0') + } + }, +} diff --git a/packages/db/script-migrations/index.ts b/packages/db/script-migrations/index.ts index e0a313b5327..ae0762265bc 100644 --- a/packages/db/script-migrations/index.ts +++ b/packages/db/script-migrations/index.ts @@ -9,6 +9,7 @@ import { projectionAclSkipUnfilledMigration } from '@sim/db/script-migrations/00 import { knowledgeProjectionAsyncMigration } from '@sim/db/script-migrations/0024_knowledge_projection_async' import { userTableSchemaForWriteMigration } from '@sim/db/script-migrations/0026_user_table_schema_for_write' import { retireKeywordWritersMigration } from '@sim/db/script-migrations/0030_retire_keyword_writers' +import { projectMembershipMigration } from '@sim/db/script-migrations/0031_project_membership' import type { Sql } from 'postgres' import { backfillTableOrderKeys } from './0001_backfill_table_order_keys' import { backfillPausedBillingAttribution } from './0002_backfill_paused_billing_attribution' @@ -55,6 +56,7 @@ export const scriptMigrations: readonly ScriptMigration[] = [ /** 0026 installs the schema guard every table row write takes before it validates. */ userTableSchemaForWriteMigration, retireKeywordWritersMigration, + projectMembershipMigration, /** * Search data retirement is an operator-run maintenance command, not a deploy step: * run `packages/db/scripts/retire-indexed-search.ts --help` for usage. diff --git a/packages/db/scripts/project-contract-required.ts b/packages/db/scripts/project-contract-required.ts index f392b48cd03..2b102112fd1 100644 --- a/packages/db/scripts/project-contract-required.ts +++ b/packages/db/scripts/project-contract-required.ts @@ -1,16 +1,13 @@ -import { verifyProjectBackfill } from '@sim/db/maintenance/project-backfill' import journal from '@sim/db/migrations/meta/_journal.json' +import { projectMembershipMigration } from '@sim/db/script-migrations/0031_project_membership' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' import postgres from 'postgres' const logger = createLogger('ProjectContractPreflight') const expansion = journal.entries.find((entry) => entry.tag === '0404_workspace_project_column') -const migration = journal.entries.find( - (entry) => entry.tag === '0405_project_membership_enforcement' -) const url = process.env.MIGRATION_DATABASE_URL || process.env.DATABASE_URL -if (!expansion || !migration || !url) +if (!expansion || !url) throw new Error( 'Project column contract preflight requires migration metadata and a database URL' ) @@ -30,9 +27,8 @@ try { if (!tables.journal) { throw new Error('Deploy the workspace Project column expansion before enforcing membership') } - const [applied] = await sql<{ expanded: boolean; complete: boolean; columnExists: boolean }[]>` + const [applied] = await sql<{ expanded: boolean; columnExists: boolean }[]>` SELECT EXISTS (SELECT 1 FROM drizzle.__drizzle_migrations WHERE created_at = ${expansion.when}) AS expanded, - EXISTS (SELECT 1 FROM drizzle.__drizzle_migrations WHERE created_at >= ${migration.when}) AS complete, EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'workspace' AND column_name = 'project_id' @@ -41,14 +37,20 @@ try { if (!applied?.expanded || !applied.columnExists) { throw new Error('Deploy the workspace Project column expansion before enforcing membership') } - required = !applied.complete - if (required) { - const counts = await verifyProjectBackfill(sql) - if (Object.values(counts).some((count) => count !== 0)) { - throw new Error( - `Project preparation is incomplete (${JSON.stringify(counts)}); run the reviewed backfill tool before deployment` - ) - } + const [scripts] = + await sql`SELECT to_regclass('public.script_migrations') IS NOT NULL AS present` + if (scripts.present) { + const [completed] = await sql`SELECT EXISTS ( + SELECT 1 FROM script_migrations WHERE name = ${projectMembershipMigration.name}) AS applied` + required = !completed.applied + } + if (!required) { + const [state] = await sql`SELECT + to_regclass('public.project_workspace') IS NULL AS retired, + EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'workspace'::regclass + AND attname = 'project_id' AND attnotnull AND NOT attisdropped) AS enforced` + if (!state.retired || !state.enforced) + throw new Error('Project migration receipt disagrees with the physical schema') } } process.stdout.write(`required=${required}\n`) diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 3d92de303f4..4f0bb7c35d5 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -11,6 +11,7 @@ import { verifyProjectBackfill, } from '@sim/db/maintenance/project-backfill' import journal from '@sim/db/migrations/meta/_journal.json' +import { runScriptMigrations, scriptMigrations } from '@sim/db/script-migrations' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { createDeferred } from '@sim/testing/helpers/deferred' import { getPostgresErrorCode } from '@sim/utils/errors' @@ -22,7 +23,7 @@ import postgres, { type Sql } from 'postgres' import { describe, expect, it } from 'vitest' const migration = await readFile( - new URL('../migrations/0405_project_membership_enforcement.sql', import.meta.url), + new URL('../maintenance/project-membership.sql', import.meta.url), 'utf8' ) @@ -93,6 +94,18 @@ async function prepareAndEnforce(sql: Sql) { await applyMigration(sql, migration) } +async function runRegisteredProjectMigration(url: string) { + const runner = postgres(url, { max: 1, onnotice: () => undefined, max_lifetime: null }) + try { + await runScriptMigrations( + runner, + scriptMigrations.filter((item) => item.name === '0031_project_membership') + ) + } finally { + await runner.end({ timeout: 2 }) + } +} + async function seed(sql: Sql) { await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Production', 'owner')` await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) VALUES ('child', 'Staging', 'owner', 'root')` @@ -103,7 +116,243 @@ async function seed(sql: Sql) { const constraintFailure = (error: unknown) => getPostgresErrorCode(error) === '23514' describe('Project expand/backfill/contract against PostgreSQL', () => { - it('requires operator preparation before SQL enforcement and leaves missing assignments untouched', async () => { + it('runs registered assignment before enforcement for forks, singletons and archived families', async () => { + await database(async (sql, url) => { + await sql`INSERT INTO workspace (id,name,owner_id,archived_at) VALUES + ('root','Root','owner',NULL), ('single','Single','owner',NULL), + ('archived','Archived','owner','2026-01-01')` + await sql`INSERT INTO workspace (id,name,owner_id,forked_from_workspace_id) + VALUES ('child','Child','owner','root')` + await runRegisteredProjectMigration(url) + expect(await sql`SELECT id FROM workspace WHERE project_id IS NULL`).toHaveLength(0) + expect(await sql`SELECT id FROM project`).toHaveLength(3) + expect( + await sql`SELECT DISTINCT project_id FROM workspace WHERE id IN ('root','child')` + ).toHaveLength(1) + expect(await sql`SELECT id FROM project WHERE archived_at IS NOT NULL`).toHaveLength(1) + expect(await sql`SELECT to_regclass('project_workspace')::text AS connector`).toEqual([ + { connector: null }, + ]) + const assignments = await sql`SELECT id,project_id FROM workspace ORDER BY id` + await runRegisteredProjectMigration(url) + expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual(assignments) + await expect( + sql`INSERT INTO workspace (id,name,owner_id) VALUES ('bad','Bad','owner')` + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23502') + }) + }) + + it('preserves legacy Project identity through a mixed fork family and a detached column assignment', async () => { + await database(async (sql, url) => { + await sql`INSERT INTO project (id,name,owner_id) VALUES + ('original','Keep original context','owner'), ('detached','Keep detached context','owner')` + await sql`INSERT INTO workspace (id,name,owner_id,project_id,forked_from_workspace_id) VALUES + ('root','Root','owner',NULL,NULL), ('child','Child','owner','original','root'), + ('unassigned','Unassigned','owner',NULL,'child'), + ('detached','Detached','owner','detached',NULL)` + await sql`INSERT INTO project_workspace (project_id,workspace_id) VALUES + ('original','root'), ('original','detached')` + await sql`INSERT INTO workflow VALUES ('flow','root',NULL)` + await runRegisteredProjectMigration(url) + expect(await sql`SELECT id,name FROM project ORDER BY id`).toEqual([ + { id: 'detached', name: 'Keep detached context' }, + { id: 'original', name: 'Keep original context' }, + ]) + expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'child', project_id: 'original' }, + { id: 'detached', project_id: 'detached' }, + { id: 'root', project_id: 'original' }, + { id: 'unassigned', project_id: 'original' }, + ]) + expect(await sql`SELECT id,workspace_id FROM workflow`).toEqual([ + { id: 'flow', workspace_id: 'root' }, + ]) + expect(await sql`SELECT to_regclass('project_workspace')::text AS connector`).toEqual([ + { connector: null }, + ]) + expect(await sql`SELECT name FROM script_migrations`).toEqual([ + { name: '0031_project_membership' }, + ]) + }) + }) + + it('copies exact legacy memberships across independent roots without changing Project lifecycle or the connector', async () => { + await database(async (sql) => { + await sql`INSERT INTO project (id,name,owner_id) VALUES ('existing','Existing','owner')` + await sql`INSERT INTO workspace (id,name,owner_id,archived_at) VALUES + ('a-archived','Archived','owner','2026-01-01'), ('b-active','Active','owner',NULL)` + await sql`INSERT INTO project_workspace (project_id,workspace_id) VALUES + ('existing','a-archived'), ('existing','b-active')` + const before = + await sql`SELECT project_id,workspace_id FROM project_workspace ORDER BY workspace_id` + const manifest = await discoverProjectBackfill(sql, 'fixture') + for (const family of manifest.families) { + expect(await assignProjectBackfillBatch(sql, [family])).toMatchObject({ + assigned: 1, + projectsCreated: 0, + }) + } + expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'a-archived', project_id: 'existing' }, + { id: 'b-active', project_id: 'existing' }, + ]) + expect(await sql`SELECT id,archived_at FROM project`).toEqual([ + { id: 'existing', archived_at: null }, + ]) + expect( + await sql`SELECT project_id,workspace_id FROM project_workspace ORDER BY workspace_id` + ).toEqual(before) + await prepareAndEnforce(sql) + }) + }) + + it('rejects changed legacy assignment evidence before writing and accepts a fresh review', async () => { + await database(async (sql) => { + await sql`INSERT INTO project (id,name,owner_id) VALUES ('first','First','owner'),('second','Second','owner')` + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('root','Root','owner')` + await sql`INSERT INTO project_workspace (project_id,workspace_id) VALUES ('first','root')` + const manifest = await discoverProjectBackfill(sql, 'fixture') + await sql`UPDATE project_workspace SET project_id = 'second' WHERE workspace_id = 'root'` + await expect(assignProjectBackfillBatch(sql, manifest.families)).rejects.toThrow('changed') + expect(await sql`SELECT project_id FROM workspace`).toEqual([{ project_id: null }]) + expect(await sql`SELECT id FROM project ORDER BY id`).toEqual([ + { id: 'first' }, + { id: 'second' }, + ]) + const reviewed = await discoverProjectBackfill(sql, 'fixture') + expect(await assignProjectBackfillBatch(sql, reviewed.families)).toMatchObject({ + assigned: 1, + projectsCreated: 0, + }) + expect(await sql`SELECT project_id FROM workspace`).toEqual([{ project_id: 'second' }]) + }) + }) + + it('releases a batch promptly when its legacy assignment row is being changed', async () => { + await database(async (sql) => { + await sql`INSERT INTO project (id,name,owner_id) VALUES ('first','First','owner'),('second','Second','owner')` + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('root','Root','owner')` + await sql`INSERT INTO project_workspace (project_id,workspace_id) VALUES ('first','root')` + const manifest = await discoverProjectBackfill(sql, 'fixture') + await sql.begin(async (tx) => { + await tx`UPDATE project_workspace SET project_id = 'second' WHERE workspace_id = 'root'` + await expect(assignProjectBackfillBatch(sql, manifest.families)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55P03' + ) + await tx`SET LOCAL lock_timeout = '100ms'` + await tx`UPDATE workspace SET name = 'Still editable' WHERE id = 'root'` + expect(await tx`SELECT project_id FROM workspace`).toEqual([{ project_id: null }]) + }) + await expect(assignProjectBackfillBatch(sql, manifest.families)).rejects.toThrow('changed') + const reviewed = await discoverProjectBackfill(sql, 'fixture') + expect(await assignProjectBackfillBatch(sql, reviewed.families)).toMatchObject({ + assigned: 1, + projectsCreated: 0, + }) + expect(await sql`SELECT project_id FROM workspace`).toEqual([{ project_id: 'second' }]) + }) + }) + + it('defers a legacy Project mutation without blocking unrelated assignment', async () => { + await database(async (sql) => { + await sql`INSERT INTO project (id,name,owner_id) VALUES ('legacy','Legacy','owner')` + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('a-legacy','Legacy','owner'),('b-free','Free','owner')` + await sql`INSERT INTO project_workspace (project_id,workspace_id) VALUES ('legacy','a-legacy')` + const manifest = await discoverProjectBackfill(sql, 'fixture') + await sql.begin(async (tx) => { + await tx`SELECT pg_advisory_xact_lock(hashtextextended('project:legacy',0))` + await expect(assignProjectBackfillBatch(sql, [manifest.families[0]])).rejects.toThrow( + 'busy' + ) + expect(await assignProjectBackfillBatch(sql, [manifest.families[1]])).toMatchObject({ + assigned: 1, + }) + expect(await sql`SELECT project_id FROM workspace WHERE id = 'a-legacy'`).toEqual([ + { project_id: null }, + ]) + }) + expect(await assignProjectBackfillBatch(sql, [manifest.families[0]])).toMatchObject({ + assigned: 1, + projectsCreated: 0, + }) + }) + }) + + it('retries the registered backfill after a failed batch without repeating committed assignments', async () => { + await database(async (sql, url) => { + await sql`INSERT INTO workspace (id,name,owner_id) SELECT lpad(n::text,3,'0'), 'Env ' || n,'owner' FROM generate_series(1,101) n` + await sql.unsafe(`CREATE FUNCTION reject_fixture() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN + IF NEW.name = 'Env 75 - Project' THEN RAISE EXCEPTION 'fixture failure'; END IF; RETURN NEW; END $$; + CREATE TRIGGER reject_fixture BEFORE INSERT ON project FOR EACH ROW EXECUTE FUNCTION reject_fixture();`) + await expect(runRegisteredProjectMigration(url)).rejects.toThrow('fixture failure') + const assignments = + await sql`SELECT id,project_id FROM workspace WHERE project_id IS NOT NULL ORDER BY id` + expect(assignments).toHaveLength(50) + expect(await sql`SELECT name FROM script_migrations`).toHaveLength(0) + expect(await sql`SELECT to_regclass('project_workspace')::text AS connector`).toEqual([ + { connector: 'project_workspace' }, + ]) + await sql`DROP TRIGGER reject_fixture ON project` + await runRegisteredProjectMigration(url) + expect( + await sql`SELECT id,project_id FROM workspace WHERE id = ANY(${assignments.map((row) => row.id)}) ORDER BY id` + ).toEqual(assignments) + expect(await sql`SELECT id FROM project`).toHaveLength(101) + expect(await sql`SELECT name FROM script_migrations`).toEqual([ + { name: '0031_project_membership' }, + ]) + }) + }) + + it('commits unrelated assignments while a family stays busy and resumes before enforcement', async () => { + await database(async (sql, url) => { + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('a-busy','Busy','owner'),('b-free','Free','owner')` + await sql.begin(async (tx) => { + await tx`SELECT pg_advisory_xact_lock_shared(hashtextextended('project-backfill:a-busy',0))` + await expect(runRegisteredProjectMigration(url)).rejects.toThrow('backfill incomplete') + expect(await sql`SELECT id FROM workspace WHERE project_id IS NOT NULL`).toEqual([ + { id: 'b-free' }, + ]) + expect(await sql`SELECT name FROM script_migrations`).toHaveLength(0) + expect(await sql`SELECT to_regclass('project_workspace')::text AS connector`).toEqual([ + { connector: 'project_workspace' }, + ]) + }) + await runRegisteredProjectMigration(url) + expect(await sql`SELECT id FROM project`).toHaveLength(2) + expect(await sql`SELECT id FROM workspace WHERE project_id IS NULL`).toHaveLength(0) + }) + }) + + it.each(['archive', 'ownership'] as const)( + 'stops the registered migration for unresolved %s before installing enforcement', + async (scenario) => { + await database(async (sql, url) => { + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('root','Root','owner')` + if (scenario === 'archive') { + await sql`UPDATE workspace SET archived_at = now()` + await sql`INSERT INTO workflow VALUES ('flow','root',NULL)` + } else { + await sql`INSERT INTO "user" VALUES ('other')` + await sql`INSERT INTO workspace (id,name,owner_id,forked_from_workspace_id) VALUES ('child','Child','other','root')` + } + await expect(runRegisteredProjectMigration(url)).rejects.toThrow( + 'Resolve Project conflicts' + ) + expect(await sql`SELECT id FROM project`).toHaveLength(0) + expect(await sql`SELECT name FROM script_migrations`).toHaveLength(0) + expect(await sql`SELECT to_regclass('project_workspace')::text AS connector`).toEqual([ + { connector: 'project_workspace' }, + ]) + if (scenario === 'archive') await sql`UPDATE workflow SET archived_at = now()` + else await sql`UPDATE workspace SET owner_id = 'owner' WHERE id = 'child'` + await runRegisteredProjectMigration(url) + expect(await sql`SELECT id FROM project`).toHaveLength(1) + }) + } + ) + + it('requires completed assignments before final enforcement and leaves missing assignments untouched', async () => { await database(async (sql) => { await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('legacy','Legacy','owner')` await expect(applyMigration(sql, migration)).rejects.toThrow('preparation is incomplete') @@ -623,12 +872,15 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sql`INSERT INTO drizzle.__drizzle_migrations VALUES (${expanded.when - 1})` await expect(run()).rejects.toMatchObject({ code: 1 }) await sql`INSERT INTO drizzle.__drizzle_migrations VALUES (${expanded.when})` + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('legacy','Legacy','owner')` expect((await run()).stdout.trim()).toBe('required=true') await sql`ALTER TABLE workspace DROP COLUMN project_id CASCADE` await expect(run()).rejects.toMatchObject({ code: 1 }) await applyMigration(sql, expansion) expect((await run()).stdout.trim()).toBe('required=true') await sql`INSERT INTO drizzle.__drizzle_migrations VALUES (${entry.when})` + expect((await run()).stdout.trim()).toBe('required=true') + await runRegisteredProjectMigration(url) expect((await run()).stdout.trim()).toBe('required=false') await sql`DELETE FROM drizzle.__drizzle_migrations WHERE created_at = ${expanded.when}` await expect(run()).rejects.toMatchObject({ code: 1 }) @@ -638,12 +890,18 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) - it('replays committed contraction without a journal receipt after recovering a failed precheck', async () => { + it('replays committed contraction when recording its script receipt fails', async () => { const directory = await mkdtemp(join(tmpdir(), 'project-contract-runner-')) try { await mkdir(join(directory, 'meta')) await writeFile(join(directory, '0404_workspace_project_column.sql'), expansion) - await writeFile(join(directory, '0405_project_membership_enforcement.sql'), migration) + await writeFile( + join(directory, '0405_project_membership_enforcement.sql'), + await readFile( + new URL('../migrations/0405_project_membership_enforcement.sql', import.meta.url), + 'utf8' + ) + ) await writeFile( join(directory, 'meta/_journal.json'), JSON.stringify({ @@ -656,64 +914,44 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) ) await database(async (sql, url) => { - const runner = postgres(url, { max: 1, onnotice: () => undefined }) - const run = () => migrate(drizzle(runner), { migrationsFolder: directory }) - try { - await sql`INSERT INTO project (id, name, owner_id) VALUES ('empty', 'Empty', 'owner')` - await expect(run()).rejects.toSatisfy( - (error: unknown) => getPostgresErrorCode(error) === '55000' - ) - const expanded = journal.entries.find( - (entry) => entry.tag === '0404_workspace_project_column' - ) - const contracted = journal.entries.find( - (entry) => entry.tag === '0405_project_membership_enforcement' + const runner = postgres(url, { max: 1, onnotice: () => undefined, max_lifetime: null }) + const run = async () => { + await migrate(drizzle(runner), { migrationsFolder: directory }) + await runScriptMigrations( + runner, + scriptMigrations.filter((item) => item.name === '0031_project_membership') ) - if (!expanded || !contracted) throw new Error('Missing Project migration metadata') - expect(await sql`SELECT created_at::text FROM drizzle.__drizzle_migrations`).toEqual([ - { created_at: String(expanded.when) }, - ]) + } + try { + await sql`INSERT INTO project (id,name,owner_id) VALUES ('empty','Empty','owner')` + await expect(run()).rejects.toThrow('validation failed') + expect(await sql`SELECT * FROM drizzle.__drizzle_migrations`).toHaveLength(2) + expect(await sql`SELECT * FROM script_migrations`).toHaveLength(0) await sql`DELETE FROM project WHERE id = 'empty'` - await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('legacy', 'Legacy', 'owner')` - await prepare(sql) - await writeFile( - join(directory, '0405_project_membership_enforcement.sql'), - `${migration}\n--> statement-breakpoint\nSELECT 1 / 0;\n` - ) - await expect(run()).rejects.toSatisfy( - (error: unknown) => getPostgresErrorCode(error) === '22012' - ) + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('legacy','Legacy','owner')` + await sql.unsafe(`CREATE FUNCTION reject_receipt() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN + RAISE EXCEPTION 'receipt failure'; END $$; + CREATE TRIGGER reject_receipt BEFORE INSERT ON script_migrations FOR EACH ROW EXECUTE FUNCTION reject_receipt();`) + await expect(run()).rejects.toThrow('receipt failure') expect(await sql`SELECT to_regclass('project_workspace')::text AS connector`).toEqual([ { connector: null }, ]) - expect(await sql`SELECT created_at::text FROM drizzle.__drizzle_migrations`).toEqual([ - { created_at: String(expanded.when) }, - ]) + expect(await sql`SELECT * FROM script_migrations`).toHaveLength(0) await expect( - sql`INSERT INTO workspace (id, name, owner_id) VALUES ('unassigned', 'Invalid', 'owner')` + sql`INSERT INTO workspace (id,name,owner_id) VALUES ('bad','Bad','owner')` ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23502') await expect( - sql`INSERT INTO workspace (id, project_id, name, owner_id) - VALUES ('missing-project', 'missing', 'Invalid', 'owner')` + sql`INSERT INTO workspace (id,project_id,name,owner_id) VALUES ('missing','missing','Missing','owner')` ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23503') - await sql.begin(async (tx) => { - await tx`INSERT INTO project (id, name, owner_id) VALUES ('after-gap', 'After gap', 'owner')` - await tx`INSERT INTO workspace (id, project_id, name, owner_id) - VALUES ('after-gap', 'after-gap', 'After gap', 'owner')` - }) - const assignments = await sql`SELECT id, project_id FROM workspace ORDER BY id` - const projects = await sql`SELECT id, name FROM project ORDER BY id` - - await writeFile(join(directory, '0405_project_membership_enforcement.sql'), migration) + const assignments = await sql`SELECT id,project_id FROM workspace ORDER BY id` + await sql`DROP TRIGGER reject_receipt ON script_migrations` await run() await run() expect(await sql`SELECT * FROM drizzle.__drizzle_migrations`).toHaveLength(2) - expect( - await sql`SELECT count(*)::int AS count FROM drizzle.__drizzle_migrations - WHERE created_at = ${contracted.when}` - ).toEqual([{ count: 1 }]) - expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual(assignments) - expect(await sql`SELECT id, name FROM project ORDER BY id`).toEqual(projects) + expect(await sql`SELECT name FROM script_migrations`).toEqual([ + { name: '0031_project_membership' }, + ]) + expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual(assignments) await expect(sql`DELETE FROM workspace`).rejects.toSatisfy(constraintFailure) } finally { await runner.end() @@ -724,7 +962,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { } }) - it.each(['workspace', 'project_workspace'] as const)( + it.each(['workspace', 'project', 'project_workspace'] as const)( 'fails contract DDL promptly on busy %s and releases partially acquired locks', async (table) => { await database(async (sql) => { @@ -748,9 +986,12 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await tx`INSERT INTO workspace (id, project_id, name, owner_id) VALUES ('live', 'live-project', 'Live', 'owner')` await tx`SELECT * FROM project` }) + expect(await sql`SELECT project_id FROM workspace WHERE id = 'live'`).toEqual([ + { project_id: 'live-project' }, + ]) expect( await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'live'` - ).toEqual([{ project_id: 'live-project' }]) + ).toHaveLength(0) } finally { release.resolve() await reader @@ -781,7 +1022,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { "SELECT count(*)::int AS count FROM pg_locks WHERE pid = $1 AND mode = 'AccessExclusiveLock' AND granted", [pid] ))[0].count; - const migration = await readFile('migrations/0405_project_membership_enforcement.sql', 'utf8'); + const migration = await readFile('maintenance/project-membership.sql', 'utf8'); for (const statement of migration.split('--> statement-breakpoint')) { await sql.unsafe(statement); if (statement.includes('LOCK TABLE workspace,')) { @@ -1168,7 +1409,8 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('existing', 'outside')` } } - const before = await sql`SELECT id, project_id FROM workspace ORDER BY id` + const before = await sql`SELECT w.id, coalesce(w.project_id, pw.project_id) AS project_id + FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` if (scenario === 'split') { expect(await discoverProjectBackfill(sql, 'fixture')).toMatchObject({ conflicts: [{ id: 'root', reason: 'Fork family spans Projects' }], @@ -1178,7 +1420,15 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { (error: unknown) => getPostgresErrorCode(error) === (scenario === 'oversized' ? '54000' : '55000') ) - expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual(before) + expect( + await sql`SELECT w.id, coalesce(w.project_id, pw.project_id) AS project_id + FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` + ).toEqual(before) + if (scenario === 'outside-family') { + expect( + await sql`SELECT id FROM workspace WHERE id IN ('root','child') AND project_id IS NOT NULL` + ).toHaveLength(0) + } }) } ) diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index 95dc91abb51..9dc1612b850 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -3,6 +3,8 @@ import { mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { fileURLToPath } from 'node:url' +import drizzleConfig from '@sim/db/drizzle.config' +import { ensureProjectArchiveRepairJournal } from '@sim/db/maintenance/project-repairs' import { prepareForcedPush } from '@sim/db/scripts/prepare-push' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { generateId } from '@sim/utils/id' @@ -33,7 +35,7 @@ describe('patched Drizzle push against PostgreSQL', () => { dialect: 'postgresql', schema: ${JSON.stringify(join(directory, 'schema.ts'))}, schemaFilter: ['public', 'old_scope', 'new_scope'], - tablesFilter: ['!script_migrations'], + tablesFilter: ${JSON.stringify(drizzleConfig.tablesFilter)}, dbCredentials: { url: process.env.DATABASE_URL }, }` ) @@ -330,12 +332,15 @@ export const workflows = pgTable('workflow', { expect(repeated.stdout).toContain('No changes detected') }, 60_000) - it('creates independent tables and enums while preserving the excluded script ledger', async () => { + it('creates independent tables and enums while preserving runner journals', async () => { await sql`CREATE TYPE old_status AS ENUM ('active')` await sql`CREATE TABLE old_records (id text PRIMARY KEY, status old_status)` await sql`INSERT INTO old_records VALUES ('old-row', 'active')` await sql`CREATE TABLE script_migrations (name text PRIMARY KEY)` await sql`INSERT INTO script_migrations VALUES ('completed-fixture-migration')` + await ensureProjectArchiveRepairJournal(sql) + await sql`INSERT INTO project_backfill_archive_repairs (workspace_id, repair) + VALUES ('pending-workspace', '{"workspaceId":"pending-workspace"}'::jsonb)` await schema(`export const status = pgEnum('new_status', ['active']) export const records = pgTable('new_records', { id: text('id').primaryKey(), status: status('status') })`) const result = push() @@ -348,6 +353,13 @@ export const records = pgTable('new_records', { id: text('id').primaryKey(), sta expect(await sql`SELECT * FROM script_migrations`).toEqual([ { name: 'completed-fixture-migration' }, ]) + expect(await sql`SELECT * FROM project_backfill_archive_repairs`).toEqual([ + { + workspace_id: 'pending-workspace', + repair: { workspaceId: 'pending-workspace' }, + completed_at: null, + }, + ]) }, 30_000) it('creates a new schema instead of moving a removed schema', async () => { diff --git a/packages/db/scripts/reconcile-project-membership.ts b/packages/db/scripts/reconcile-project-membership.ts index 95602c26ad4..77ae76bb7a3 100644 --- a/packages/db/scripts/reconcile-project-membership.ts +++ b/packages/db/scripts/reconcile-project-membership.ts @@ -1,4 +1,4 @@ -import { readFile } from 'node:fs/promises' +import { enforceProjectMembership } from '@sim/db/maintenance/project-enforcement' import { createLogger } from '@sim/logger' import { getErrorMessage, getPostgresErrorCode } from '@sim/utils/errors' import postgres from 'postgres' @@ -19,23 +19,12 @@ try { ` if (state.workspace && (state.connector || !state.required || !state.project)) { throw new Error( - 'Existing Project databases must complete the reviewed preparation and contract migration 0405 before schema push. Use the phased migration path for retained data; schema push cannot bypass the writer-drain requirement' + 'Existing Project databases must complete the Project script migration 0031 before schema push. Use the phased migration path for retained data; schema push cannot bypass the writer-drain requirement' ) } if (!process.argv.includes('--prepare') && state.workspace && state.project) { // Drizzle cannot express lifecycle triggers; fresh push uses the same enforcement as migrations. - const source = await readFile( - new URL('../migrations/0405_project_membership_enforcement.sql', import.meta.url), - 'utf8' - ) - const connection = await sql.reserve() - try { - for (const statement of source.split('--> statement-breakpoint')) { - if (statement.trim()) await connection.unsafe(statement) - } - } finally { - connection.release() - } + await enforceProjectMembership(sql) logger.info('Project membership validation and lifecycle enforcement completed') } } catch (error) { diff --git a/packages/db/scripts/workspace-project-column.integration.ts b/packages/db/scripts/workspace-project-column.integration.ts index 421229c2afb..046345e0c0f 100644 --- a/packages/db/scripts/workspace-project-column.integration.ts +++ b/packages/db/scripts/workspace-project-column.integration.ts @@ -30,412 +30,146 @@ async function fixture(run: (sql: postgres.Sql) => Promise): Promise }) } -async function installBridgeBeforeCopy(sql: postgres.Sql): Promise { - for (const statement of migration.split('--> statement-breakpoint')) { - if (statement.trim().startsWith('CALL pg_temp.copy_workspace_projects()')) return - if (statement.trim()) await sql.unsafe(statement) - } - throw new Error('Migration has no Project copy entry point') -} - -async function withConnections( - sql: postgres.Sql, - run: (writer: postgres.Sql, fork: postgres.Sql, observer: postgres.Sql) => Promise -): Promise { - const [scope] = await sql<{ schema: string }[]>`SELECT current_schema() AS schema` - if (!scope) throw new Error('Migration fixture has no current schema') - const writer = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => {} }) - const fork = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => {} }) - const observer = postgres(readTestDatabaseUrl(), { max: 1, onnotice: () => {} }) - try { - for (const connection of [writer, fork, observer]) { - await connection`SET search_path = ${connection(scope.schema)}` - await connection`SET statement_timeout = '10s'` - } - await run(writer, fork, observer) - } finally { - await Promise.all([writer.end(), fork.end(), observer.end()]) - } -} - -async function backendPid(sql: postgres.Sql): Promise { - const [backend] = await sql<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` - if (!backend) throw new Error('Migration fixture connection has no backend PID') - return backend.pid -} - -async function expectBlockedBy( - observer: postgres.Sql, - waitingPid: number, - blockingPid: number -): Promise { - await expect - .poll( - async () => { - const [state] = await observer<{ waiting: boolean }[]>` - SELECT ${blockingPid} = ANY(pg_blocking_pids(${waitingPid})) AS waiting` - return state?.waiting - }, - { timeout: 5_000 } - ) - .toBe(true) -} - describe('workspace Project column compatibility migration', () => { - it('copies existing memberships and permits old workspace-before-Project creation', async () => { + it('leaves legacy rows unassigned and accepts old workspace-before-Project creation', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) - await sql.begin(async (tx) => { await tx`INSERT INTO workspace (id) VALUES ('old-writer')` await tx`INSERT INTO project VALUES ('new-project')` await tx`INSERT INTO project_workspace VALUES ('new-project', 'old-writer')` }) - - expect( - await sql`SELECT w.id, w.project_id, pw.project_id AS membership, w.forked_from_workspace_id AS parent - FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` - ).toEqual([ - { id: 'old-writer', project_id: 'new-project', membership: 'new-project', parent: null }, - { id: 'personal', project_id: 'private', membership: 'private', parent: null }, - { id: 'production', project_id: 'family', membership: 'family', parent: null }, - { id: 'staging', project_id: 'family', membership: 'family', parent: 'production' }, + expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'old-writer', project_id: null }, + { id: 'personal', project_id: null }, + { id: 'production', project_id: null }, + { id: 'staging', project_id: null }, ]) + expect( + await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'old-writer'` + ).toEqual([{ project_id: 'new-project' }]) }) }) - it('mirrors old connector reassignment and removal while rejecting workspace identity changes', async () => { + it('new column writes do not create or overwrite legacy connector assignments', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) - await sql`INSERT INTO workspace (id) VALUES ('moved-target')` - await sql`UPDATE project_workspace SET project_id = 'private' WHERE workspace_id = 'staging'` - expect(await sql`SELECT project_id FROM workspace WHERE id = 'staging'`).toEqual([ - { project_id: 'private' }, - ]) - - await expect(sql`UPDATE project_workspace SET workspace_id = 'moved-target' - WHERE workspace_id = 'staging'`).rejects.toSatisfy( - (error: unknown) => getPostgresErrorCode(error) === '55000' - ) + await sql`INSERT INTO workspace (id, project_id) VALUES ('new-writer', 'private')` + await sql`UPDATE workspace SET project_id = 'private', forked_from_workspace_id = NULL + WHERE id = 'staging'` expect( - await sql`SELECT id, project_id FROM workspace WHERE id IN ('staging', 'moved-target') ORDER BY id` + await sql`SELECT project_id, workspace_id FROM project_workspace ORDER BY workspace_id` ).toEqual([ - { id: 'moved-target', project_id: null }, - { id: 'staging', project_id: 'private' }, + { project_id: 'private', workspace_id: 'personal' }, + { project_id: 'family', workspace_id: 'production' }, + { project_id: 'family', workspace_id: 'staging' }, ]) - expect( - await sql`SELECT project_id, workspace_id FROM project_workspace WHERE workspace_id = 'staging'` - ).toEqual([{ project_id: 'private', workspace_id: 'staging' }]) - - await sql`DELETE FROM project_workspace WHERE workspace_id = 'staging'` - expect(await sql`SELECT project_id FROM workspace WHERE id = 'staging'`).toEqual([ + await sql`UPDATE project_workspace SET project_id = 'family' WHERE workspace_id = 'personal'` + expect(await sql`SELECT project_id FROM workspace WHERE id = 'personal'`).toEqual([ { project_id: null }, ]) - expect(await sql`SELECT workspace_id FROM project_workspace ORDER BY workspace_id`).toEqual([ - { workspace_id: 'personal' }, - { workspace_id: 'production' }, - ]) - }) - }) - - it('mirrors new column writes, fork detach and workspace cascades into old readers', async () => { - await fixture(async (sql) => { - await applyMigration(sql, migration) - await sql`INSERT INTO workspace (id, project_id, forked_from_workspace_id) - VALUES ('new-fork', 'family', 'production')` - expect( - await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'new-fork'` - ).toEqual([{ project_id: 'family' }]) - await sql`UPDATE workspace SET project_id = 'private' WHERE id = 'new-fork'` - expect( - await sql`SELECT project_id FROM project_workspace WHERE workspace_id = 'new-fork'` - ).toEqual([{ project_id: 'private' }]) - - await sql`UPDATE workspace SET project_id = NULL, forked_from_workspace_id = NULL - WHERE id = 'new-fork'` - expect( - await sql`SELECT workspace_id FROM project_workspace WHERE workspace_id = 'new-fork'` - ).toEqual([]) - expect( - await sql`SELECT project_id, forked_from_workspace_id FROM workspace WHERE id = 'new-fork'` - ).toEqual([{ project_id: null, forked_from_workspace_id: null }]) - - await sql`DELETE FROM workspace WHERE id = 'production'` - expect(await sql`SELECT workspace_id FROM project_workspace ORDER BY workspace_id`).toEqual([ - { workspace_id: 'personal' }, - { workspace_id: 'staging' }, - ]) - expect( - await sql`SELECT project_id, forked_from_workspace_id FROM workspace WHERE id = 'staging'` - ).toEqual([{ project_id: 'family', forked_from_workspace_id: null }]) }) }) - it('rejects missing Projects atomically and rolls back both representations with the writer', async () => { + it('rejects dangling new assignments and protects column-only Projects from old teardown', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) await expect( - sql`INSERT INTO workspace (id, project_id) VALUES ('valid-new', 'family'), ('invalid-new', 'missing')` + sql`INSERT INTO workspace (id, project_id) VALUES ('invalid', 'missing')` ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23503') - expect(await sql`SELECT id FROM workspace WHERE id IN ('valid-new', 'invalid-new')`).toEqual( - [] - ) - expect( - await sql`SELECT workspace_id FROM project_workspace WHERE workspace_id = 'valid-new'` - ).toEqual([]) - + await sql`INSERT INTO project VALUES ('column-only')` + await sql`INSERT INTO workspace (id, project_id) VALUES ('new-writer', 'column-only')` await expect( sql.begin(async (tx) => { - await tx`UPDATE workspace SET project_id = 'private' WHERE id = 'staging'` - await tx`UPDATE workspace SET project_id = 'missing' WHERE id = 'production'` + await tx`DELETE FROM project_workspace WHERE project_id = 'column-only'` + await tx`DELETE FROM project WHERE id = 'column-only'` }) ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23503') - expect( - await sql`SELECT w.id, w.project_id, pw.project_id AS membership - FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE w.id IN ('production', 'staging') ORDER BY w.id` - ).toEqual([ - { id: 'production', project_id: 'family', membership: 'family' }, - { id: 'staging', project_id: 'family', membership: 'family' }, + expect(await sql`SELECT id FROM project WHERE id = 'column-only'`).toEqual([ + { id: 'column-only' }, + ]) + expect(await sql`SELECT project_id FROM workspace WHERE id = 'new-writer'`).toEqual([ + { project_id: 'column-only' }, ]) }) }) - it('replays without restoring a detached fork or overwriting a newer Project assignment', async () => { + it('keeps old teardown valid for untouched legacy workspaces', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) - await sql`UPDATE workspace SET project_id = 'private' WHERE id = 'production'` - await sql`UPDATE workspace SET project_id = NULL, forked_from_workspace_id = NULL - WHERE id = 'staging'` - - await applyMigration(sql, migration) - await applyMigration(sql, migration) - - expect( - await sql`SELECT w.id, w.project_id, pw.project_id AS membership, w.forked_from_workspace_id AS parent - FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` - ).toEqual([ - { id: 'personal', project_id: 'private', membership: 'private', parent: null }, - { id: 'production', project_id: 'private', membership: 'private', parent: null }, - { id: 'staging', project_id: null, membership: null, parent: null }, - ]) - - await sql`INSERT INTO project_workspace VALUES ('family', 'staging')` - expect(await sql`SELECT project_id FROM workspace WHERE id = 'staging'`).toEqual([ - { project_id: 'family' }, + await sql.begin(async (tx) => { + await tx`DELETE FROM project_workspace WHERE project_id = 'private'` + await tx`DELETE FROM project WHERE id = 'private'` + await tx`DELETE FROM workspace WHERE id = 'personal'` + }) + expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'production', project_id: null }, + { id: 'staging', project_id: null }, ]) }) }) - it('allows several short copy batches to exceed one transaction timeout in total', async () => { + it('replays without copying stale legacy assignments over columns or detached forks', async () => { await fixture(async (sql) => { - await sql`ALTER TABLE workspace ADD COLUMN project_id text` - await sql`INSERT INTO workspace (id) - SELECT 'batch-' || lpad(n::text, 4, '0') FROM generate_series(1, 251) n` - await sql`INSERT INTO project_workspace (project_id, workspace_id) - SELECT 'family', id FROM workspace WHERE id LIKE 'batch-%'` - await sql.unsafe(`CREATE FUNCTION delay_copy_batch() RETURNS trigger LANGUAGE plpgsql AS $$ - BEGIN - IF NEW.id IN ('batch-0001', 'batch-0101', 'batch-0201') THEN PERFORM pg_sleep(2); END IF; - RETURN NEW; - END; - $$`) - await sql`CREATE TRIGGER delay_copy_batch BEFORE UPDATE OF project_id ON workspace - FOR EACH ROW EXECUTE FUNCTION delay_copy_batch()` await applyMigration(sql, migration) - expect( - await sql`SELECT count(*)::int AS count FROM workspace w - JOIN project_workspace pw ON pw.workspace_id = w.id AND pw.project_id = w.project_id` - ).toEqual([{ count: 254 }]) - }) - }, 20000) - - it('commits bounded copy batches, retains completed work on failure and safely resumes', async () => { - await fixture(async (sql) => { - await sql`ALTER TABLE workspace ADD COLUMN project_id text` - await sql`INSERT INTO workspace (id) - SELECT 'batch-' || lpad(n::text, 4, '0') FROM generate_series(1, 251) n` - await sql`INSERT INTO project_workspace (project_id, workspace_id) - SELECT 'family', id FROM workspace WHERE id LIKE 'batch-%'` - await sql`CREATE TABLE copy_commit_audit (workspace_id text PRIMARY KEY, transaction_id bigint NOT NULL)` - await sql.unsafe(`CREATE FUNCTION record_copy_transaction() RETURNS trigger LANGUAGE plpgsql AS $$ - BEGIN - IF NEW.project_id IS NOT NULL AND OLD.project_id IS DISTINCT FROM NEW.project_id THEN - INSERT INTO copy_commit_audit VALUES (NEW.id, txid_current()) ON CONFLICT DO NOTHING; - END IF; - RETURN NEW; - END; - $$`) - await sql`CREATE TRIGGER record_copy_transaction AFTER UPDATE OF project_id ON workspace - FOR EACH ROW EXECUTE FUNCTION record_copy_transaction()` - await sql`ALTER TABLE workspace ADD CONSTRAINT reject_later_copy - CHECK (id <> 'batch-0201' OR project_id IS NULL)` - - await expect(applyMigration(sql, migration)).rejects.toSatisfy( - (error: unknown) => getPostgresErrorCode(error) === '23514' - ) - expect( - await sql`SELECT count(*)::int AS count FROM workspace WHERE project_id IS NOT NULL` - ).toEqual([{ count: 200 }]) - expect( - await sql`SELECT count(*)::int AS count FROM copy_commit_audit GROUP BY transaction_id ORDER BY transaction_id` - ).toEqual([{ count: 100 }, { count: 100 }]) - expect(await sql`SELECT project_id FROM workspace WHERE id = 'batch-0201'`).toEqual([ - { project_id: null }, - ]) - - await sql`ALTER TABLE workspace DROP CONSTRAINT reject_later_copy` - await sql`UPDATE project_workspace SET project_id = 'private' WHERE workspace_id = 'batch-0001'` - await sql`DELETE FROM project_workspace WHERE workspace_id = 'batch-0002'` + await sql`UPDATE workspace SET project_id = 'private', forked_from_workspace_id = NULL + WHERE id = 'staging'` await applyMigration(sql, migration) await applyMigration(sql, migration) - expect( - await sql`SELECT w.id, w.project_id, pw.project_id AS membership FROM workspace w - LEFT JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE w.id IN ('batch-0001', 'batch-0002', 'batch-0201', 'batch-0251') ORDER BY w.id` + await sql`SELECT w.id, w.project_id, pw.project_id AS legacy, + w.forked_from_workspace_id AS parent FROM workspace w + JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` ).toEqual([ - { id: 'batch-0001', project_id: 'private', membership: 'private' }, - { id: 'batch-0002', project_id: null, membership: null }, - { id: 'batch-0201', project_id: 'family', membership: 'family' }, - { id: 'batch-0251', project_id: 'family', membership: 'family' }, + { id: 'personal', project_id: null, legacy: 'private', parent: null }, + { id: 'production', project_id: null, legacy: 'family', parent: null }, + { id: 'staging', project_id: 'private', legacy: 'family', parent: null }, ]) - expect( - await sql`SELECT count(*)::int AS count FROM workspace w - LEFT JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE w.project_id IS DISTINCT FROM pw.project_id` - ).toEqual([{ count: 0 }]) - expect( - await sql`SELECT count(*)::int AS count FROM copy_commit_audit GROUP BY transaction_id ORDER BY transaction_id` - ).toEqual([{ count: 100 }, { count: 100 }, { count: 54 }]) }) }) - it.each([ - { change: 'legacy reassignment', projectId: 'private' }, - { change: 'legacy removal', projectId: null }, - ])( - 'preserves $change and a waiting fork detach during concurrent copy', - async ({ projectId }) => { - await fixture(async (sql) => { - await installBridgeBeforeCopy(sql) - await sql`UPDATE workspace SET project_id = 'family' WHERE id = 'staging'` - await withConnections(sql, async (writer, fork, observer) => { - const writerPid = await backendPid(writer) - const forkPid = await backendPid(fork) - const copyPid = await backendPid(sql) - let forkLock: Promise | undefined - let copy: Promise | undefined - await writer.unsafe('BEGIN') - await fork.unsafe('BEGIN') - try { - await writer`SELECT pg_advisory_xact_lock(hashtextextended('project:family', 0))` - if (projectId === null) { - await writer`DELETE FROM project_workspace WHERE workspace_id = 'production'` - } else { - await writer`UPDATE project_workspace SET project_id = ${projectId} - WHERE workspace_id = 'production'` - } - forkLock = - fork`SELECT pg_advisory_xact_lock(hashtextextended('project:family', 0))`.then( - () => undefined - ) - void forkLock.catch(() => undefined) - await expectBlockedBy(observer, forkPid, writerPid) - - copy = sql.unsafe('CALL pg_temp.copy_workspace_projects()').then(() => undefined) - void copy.catch(() => undefined) - await expect - .poll( - async () => { - const [activity] = await observer<{ wait_event: string | null }[]>` - SELECT wait_event FROM pg_stat_activity WHERE pid = ${copyPid}` - return activity?.wait_event - }, - { timeout: 5_000 } - ) - .toBe('PgSleep') - - await writer.unsafe('COMMIT') - await forkLock - await fork`UPDATE workspace SET project_id = NULL, forked_from_workspace_id = NULL - WHERE id = 'staging'` - await fork.unsafe('COMMIT') - await copy - - expect( - await observer`SELECT w.id, w.project_id, pw.project_id AS membership, - w.forked_from_workspace_id AS parent FROM workspace w - LEFT JOIN project_workspace pw ON pw.workspace_id = w.id - WHERE w.id IN ('production', 'staging') ORDER BY w.id` - ).toEqual([ - { id: 'production', project_id: projectId, membership: projectId, parent: null }, - { id: 'staging', project_id: null, membership: null, parent: null }, - ]) - } finally { - await writer.unsafe('ROLLBACK') - await forkLock?.catch(() => undefined) - await fork.unsafe('ROLLBACK') - await copy?.catch(() => undefined) - } - }) - }) - } - ) - - it('allows old account teardown to delete a Project before its populated workspace', async () => { + it('refuses a busy expansion immediately without blocking unrelated writes', async () => { await fixture(async (sql) => { + const [scope] = await sql<{ schema: string }[]>`SELECT current_schema() AS schema` + const writer = postgres(readTestDatabaseUrl(), { max: 1 }) + try { + await writer`SET search_path = ${writer(scope.schema)}` + await writer.unsafe('BEGIN') + await writer`UPDATE workspace SET forked_from_workspace_id = NULL WHERE id = 'personal'` + await expect(applyMigration(sql, migration)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55P03' + ) + await sql.unsafe('ROLLBACK') + await sql`INSERT INTO workspace (id) VALUES ('unblocked-writer')` + } finally { + await writer.unsafe('ROLLBACK') + await writer.end() + } await applyMigration(sql, migration) - - await sql.begin(async (tx) => { - await tx`DELETE FROM project_workspace WHERE project_id = 'private'` - await tx`DELETE FROM project WHERE id = 'private'` - await tx`DELETE FROM workspace WHERE id = 'personal'` - }) - - expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ - { id: 'production', project_id: 'family' }, - { id: 'staging', project_id: 'family' }, - ]) - expect(await sql`SELECT id FROM project`).toEqual([{ id: 'family' }]) - expect(await sql`SELECT workspace_id FROM project_workspace ORDER BY workspace_id`).toEqual([ - { workspace_id: 'production' }, - { workspace_id: 'staging' }, + expect(await sql`SELECT project_id FROM workspace WHERE id = 'unblocked-writer'`).toEqual([ + { project_id: null }, ]) }) }) - it('repairs an interrupted concurrent index build on replay without changing Project assignments', async () => { + it('repairs an interrupted index build without changing assignments', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) + await sql`UPDATE workspace SET project_id = 'family' WHERE id IN ('production', 'staging')` await sql`DROP INDEX CONCURRENTLY workspace_project_id_id_idx` await expect( sql`CREATE UNIQUE INDEX CONCURRENTLY workspace_project_id_id_idx ON workspace(project_id)` ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23505') - expect( - await sql`SELECT indisvalid FROM pg_index - WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` - ).toEqual([{ indisvalid: false }]) - await applyMigration(sql, migration) - await applyMigration(sql, migration) - expect( await sql`SELECT indisvalid, indisunique FROM pg_index - WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` + WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` ).toEqual([{ indisvalid: true, indisunique: false }]) expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ - { id: 'personal', project_id: 'private' }, + { id: 'personal', project_id: null }, { id: 'production', project_id: 'family' }, { id: 'staging', project_id: 'family' }, ]) - await sql.begin(async (tx) => { - await tx`INSERT INTO workspace (id, project_id, forked_from_workspace_id) - VALUES ('new-fork', 'family', 'production')` - }) - expect( - await sql`SELECT count(*)::int AS count FROM workspace WHERE project_id = 'family'` - ).toEqual([{ count: 3 }]) }) }) }) From 7613c1421e3c2931466f1a67049f3328439f9bdd Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 13:23:01 -0700 Subject: [PATCH 074/113] test(projects): verify column-only archive repair assignment --- .../projects/__integration__/backfill-repair.integration.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts index cfeafde1a97..cf3553824bd 100644 --- a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -274,7 +274,8 @@ describe('Operator archive repair against the full compatible schema', () => { { archived_at: '2026-05-01 00:00:00' }, ]) expect(await client`SELECT project_id FROM workspace`).toEqual( - await client`SELECT project_id FROM project_workspace` + await client`SELECT id AS project_id FROM project` ) + expect(await client`SELECT project_id FROM project_workspace`).toHaveLength(0) }, 120000) }) From f0ce089972476102b366604683a14e1225e536cc Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 14:26:31 -0700 Subject: [PATCH 075/113] fix(projects): retain healthy indexes during schema push --- packages/db/scripts/push.integration.ts | 47 ++++++++++++++++++- .../scripts/reconcile-project-membership.ts | 8 ++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index 3c2c5d56879..62eab9391dc 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -58,7 +58,7 @@ describe('patched Drizzle push against PostgreSQL', () => { async function schema(source: string) { await writeFile( join(directory, 'schema.ts'), - `import { pgTable, pgSchema, pgEnum, text, integer, bigint, boolean, check } from ${JSON.stringify(import.meta.resolve('drizzle-orm/pg-core'))} + `import { pgTable, pgSchema, pgEnum, text, integer, bigint, boolean, check, index } from ${JSON.stringify(import.meta.resolve('drizzle-orm/pg-core'))} import { sql } from ${JSON.stringify(import.meta.resolve('drizzle-orm'))} ${source}` ) @@ -195,7 +195,7 @@ export const knowledgeBases = pgTable('knowledge_base', { await schema(`export const projects = pgTable('project', { id: text('id').primaryKey() }) export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), forkedFromWorkspaceId: text('forked_from_workspace_id'), projectId: text('project_id'), -}) +}, (table) => [index('workspace_project_id_id_idx').on(table.projectId, table.id)]) export const memberships = pgTable('project_workspace', { projectId: text('project_id').notNull(), workspaceId: text('workspace_id').notNull().unique(), })`) @@ -212,8 +212,15 @@ export const memberships = pgTable('project_workspace', { ]) await sql`UPDATE project_workspace SET project_id = 'singleton' WHERE workspace_id = 'fork'` await sql`UPDATE workspace SET project_id = 'family' WHERE id = 'standalone'` + const indexBeforeReplay = await sql`SELECT indexrelid, indisvalid FROM pg_index + WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` + expect(indexBeforeReplay).toEqual([{ indexrelid: expect.any(Number), indisvalid: true }]) const repeated = runPush(['--force']) expect(repeated.error, repeated.stderr).toBeUndefined() + expect( + await sql`SELECT indexrelid, indisvalid FROM pg_index + WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` + ).toEqual(indexBeforeReplay) expect( await sql`SELECT w.id, w.project_id, pw.project_id AS legacy FROM workspace w JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` @@ -224,6 +231,42 @@ export const memberships = pgTable('project_workspace', { ]) }, 60_000) + it('repairs an interrupted Project index build during schema-push reconciliation', async () => { + await sql`CREATE TABLE project (id text PRIMARY KEY)` + await sql`CREATE TABLE workspace (id text PRIMARY KEY, project_id text)` + await sql`CREATE TABLE project_workspace (project_id text NOT NULL, workspace_id text NOT NULL UNIQUE)` + await sql`INSERT INTO project VALUES ('family')` + await sql`INSERT INTO workspace VALUES ('root', 'family'), ('fork', 'family')` + await expect( + sql`CREATE UNIQUE INDEX CONCURRENTLY workspace_project_id_id_idx ON workspace(project_id)` + ).rejects.toMatchObject({ code: '23505' }) + expect( + await sql`SELECT indisvalid FROM pg_index WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` + ).toEqual([{ indisvalid: false }]) + const result = spawnSync( + 'bun', + [ + '--no-env-file', + fileURLToPath(new URL('./reconcile-project-membership.ts', import.meta.url)), + ], + { + env: { ...process.env, DATABASE_URL: fixtureUrl, MIGRATION_DATABASE_URL: fixtureUrl }, + encoding: 'utf8', + timeout: 15_000, + } + ) + expect(result.error).toBeUndefined() + expect(result.status, result.stdout + result.stderr).toBe(0) + expect( + await sql`SELECT indisvalid, indisunique FROM pg_index + WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` + ).toEqual([{ indisvalid: true, indisunique: false }]) + expect(await sql`SELECT id, project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'fork', project_id: 'family' }, + { id: 'root', project_id: 'family' }, + ]) + }) + it('refuses a schema downgrade before recreating the retired Project connector', async () => { await sql`CREATE TABLE project (id text PRIMARY KEY)` await sql`CREATE TABLE workspace (id text PRIMARY KEY, project_id text NOT NULL)` diff --git a/packages/db/scripts/reconcile-project-membership.ts b/packages/db/scripts/reconcile-project-membership.ts index 6f5d9a51872..617177641b5 100644 --- a/packages/db/scripts/reconcile-project-membership.ts +++ b/packages/db/scripts/reconcile-project-membership.ts @@ -31,6 +31,14 @@ try { const connection = await sql.reserve() try { for (const statement of source.split('--> statement-breakpoint')) { + if (statement.includes('DROP INDEX CONCURRENTLY IF EXISTS "workspace_project_id_id_idx"')) { + // Migration retries rebuild interrupted indexes; routine push keeps healthy indexes intact. + const [index] = await connection<{ healthy: boolean }[]>` + SELECT indisvalid AND indisready AS healthy FROM pg_index + WHERE indexrelid = to_regclass('public.workspace_project_id_id_idx') + ` + if (index?.healthy) continue + } if (statement.trim()) await connection.unsafe(statement) } } finally { From e92ba0bda51b8870af075c5b53605e191f7e2f18 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 14:29:18 -0700 Subject: [PATCH 076/113] ci(projects): use standard integration coverage for enforcement --- .github/workflows/checks.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 97f0a09d45f..7210aa37488 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -121,15 +121,6 @@ jobs: bun run test --mode integration lib/billing/core/usage-log.integration.ts --outputFile.json=test-results/integration-pg16.json - - name: Verify Project preparation and enforcement on PostgreSQL 16 - if: matrix.provision == 'push' && matrix.shard == 1 - working-directory: packages/db - env: - TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5433/sim_test - run: >- - bun run test --mode integration scripts/project-contract.integration.ts - --outputFile.json=test-results/integration-project-contract-pg16.json - - name: Upload integration test reports if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 From 0dfc7abc1fd49dc9f578c4b0ae96d25d50a8698c Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 16:23:55 -0700 Subject: [PATCH 077/113] fix(projects): correct rollout fixtures and schema-push index --- .../__integration__/foundation.integration.ts | 23 +++++++++++++------ .../organization-workspaces.integration.ts | 8 ++++++- .../db/migrations/meta/0404_snapshot.json | 2 +- packages/db/schema.ts | 2 +- packages/db/scripts/push.integration.ts | 2 +- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index d1ddd1cd515..19f33d6fc6d 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -207,17 +207,18 @@ async function fixture(org = true, count = 2) { /** * Waits until the operation under test is blocked behind `blockerPid`: a session whose - * current statement contains `waitingIn` (an advisory lock tag or row-lock clause), so an - * unrelated waiter cannot release the barrier early. + * current statement contains `waitingIn` (an advisory lock tag or row-lock clause). + * When the caller owns the transaction, `waitingPid` identifies that exact backend. */ -async function waitUntilBlockedBy(blockerPid: number, waitingIn: string) { +async function waitUntilBlockedBy(blockerPid: number, waitingIn: string, waitingPid?: number) { await expect .poll( async () => ( await db.execute(sql` SELECT 1 FROM pg_stat_activity - WHERE ${blockerPid} = ANY(pg_blocking_pids(pid)) + WHERE ${waitingPid === undefined ? sql`true` : sql`pid = ${waitingPid}`} + AND ${blockerPid} = ANY(pg_blocking_pids(pid)) AND position(${waitingIn.toLowerCase()} in lower(query)) > 0 `) ).length, @@ -1394,14 +1395,22 @@ describe('Project foundation at the database and application boundary', () => { ) }) const blocker = await held.promise + const detachPid = createDeferred() const detach = db - .transaction((tx) => splitForkProject(tx, f.ids[1])) + .transaction(async (tx) => { + const [backend] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + detachPid.resolve(backend.pid) + return splitForkProject(tx, f.ids[1]) + }) .then( () => null, - (error: unknown) => error + (error: unknown) => { + detachPid.reject(error) + return error + } ) try { - await waitUntilBlockedBy(blocker, "lock='project'") + await waitUntilBlockedBy(blocker, "lock='project'", await detachPid.promise) } finally { release.resolve() await legacy diff --git a/apps/sim/lib/workspaces/organization-workspaces.integration.ts b/apps/sim/lib/workspaces/organization-workspaces.integration.ts index d2a2fa1e916..805331de6e0 100644 --- a/apps/sim/lib/workspaces/organization-workspaces.integration.ts +++ b/apps/sim/lib/workspaces/organization-workspaces.integration.ts @@ -43,10 +43,16 @@ beforeAll(async () => { CREATE TABLE workspace ( id text PRIMARY KEY, name text, owner_id text, organization_id text, workspace_mode text, project_id text NOT NULL REFERENCES project(id) ON DELETE RESTRICT, + forked_from_workspace_id text, billed_account_user_id text, allow_personal_api_keys boolean DEFAULT true, archived_at timestamp, organization_assigned_at timestamp, updated_at timestamp, storage_used_bytes bigint NOT NULL ); + CREATE TABLE project_workspace ( + project_id text NOT NULL REFERENCES project(id) ON DELETE RESTRICT, + workspace_id text NOT NULL UNIQUE REFERENCES workspace(id) ON DELETE CASCADE, + PRIMARY KEY (project_id, workspace_id) + ); CREATE TABLE permissions ( id text PRIMARY KEY, user_id text, entity_type text, entity_id text, permission_type text, created_at timestamp, updated_at timestamp, UNIQUE(user_id, entity_type, entity_id) @@ -64,7 +70,7 @@ beforeAll(async () => { beforeEach(async () => { await connection.unsafe(` - TRUNCATE member, organization, project, permission_group, invitation, user_stats, workspace, permissions, + TRUNCATE member, organization, project, project_workspace, permission_group, invitation, user_stats, workspace, permissions, workspace_files, knowledge_base, document, knowledge_connector; INSERT INTO member VALUES ('owner-membership', 'org', 'org-owner', 'owner'); INSERT INTO organization VALUES ('org', 40); diff --git a/packages/db/migrations/meta/0404_snapshot.json b/packages/db/migrations/meta/0404_snapshot.json index 264a1375ce6..603cc7353e7 100644 --- a/packages/db/migrations/meta/0404_snapshot.json +++ b/packages/db/migrations/meta/0404_snapshot.json @@ -27101,7 +27101,7 @@ } ], "isUnique": false, - "concurrently": false, + "concurrently": true, "method": "btree", "with": {} }, diff --git a/packages/db/schema.ts b/packages/db/schema.ts index cafd917b654..acdfebe4180 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2061,7 +2061,7 @@ export const workspace = pgTable( (table) => ({ ownerIdIdx: index('workspace_owner_id_idx').on(table.ownerId), organizationIdIdx: index('workspace_organization_id_idx').on(table.organizationId), - projectIdIdx: index('workspace_project_id_id_idx').on(table.projectId, table.id), + projectIdIdx: index('workspace_project_id_id_idx').on(table.projectId, table.id).concurrently(), nonNegativeStorage: check( 'workspace_storage_used_bytes_non_negative', sql`${table.storageUsedBytes} >= 0` diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index 62eab9391dc..a0d60010c3c 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -195,7 +195,7 @@ export const knowledgeBases = pgTable('knowledge_base', { await schema(`export const projects = pgTable('project', { id: text('id').primaryKey() }) export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), forkedFromWorkspaceId: text('forked_from_workspace_id'), projectId: text('project_id'), -}, (table) => [index('workspace_project_id_id_idx').on(table.projectId, table.id)]) +}, (table) => [index('workspace_project_id_id_idx').on(table.projectId, table.id).concurrently()]) export const memberships = pgTable('project_workspace', { projectId: text('project_id').notNull(), workspaceId: text('workspace_id').notNull().unique(), })`) From bdf50b29c7c1c4b06c707db334278684d023928d Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 16:37:01 -0700 Subject: [PATCH 078/113] fix(projects): reconcile assigned environments before enforcement --- .../backfill-repair.integration.ts | 184 +++++++++- apps/sim/lib/projects/backfill-repair.ts | 124 ++++++- apps/sim/lib/projects/membership.ts | 8 +- apps/sim/lib/workflows/lifecycle.ts | 9 +- apps/sim/scripts/backfill-projects.ts | 75 +++- .../db/maintenance/project-backfill.test.ts | 16 + packages/db/maintenance/project-backfill.ts | 334 ++++++++++++++++-- packages/db/maintenance/project-repairs.ts | 6 +- .../0031_project_membership.ts | 15 +- .../scripts/project-contract.integration.ts | 189 ++++++++-- packages/db/scripts/push.integration.ts | 51 ++- packages/db/testing/workspace-fixtures.ts | 31 +- 12 files changed, 942 insertions(+), 100 deletions(-) create mode 100644 packages/db/maintenance/project-backfill.test.ts diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts index cf3553824bd..8e3772e4ed7 100644 --- a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -137,7 +137,7 @@ describe('Operator archive repair against the full compatible schema', () => { await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) VALUES (${ownerId},'Owner',${`${lock}@fixture.test`},true,now(),now())` await client`INSERT INTO project (id,name,owner_id,archived_at) - VALUES (${projectId},'Archived Project',${ownerId},'2026-05-01')` + VALUES (${projectId},'Stale active Project',${ownerId},NULL)` await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id,archived_at) VALUES (${busyId},'Busy',${ownerId},${ownerId},${projectId},'2026-05-01'), (${freeId},'Free',${ownerId},${ownerId},NULL,'2026-05-01')` @@ -167,6 +167,9 @@ describe('Operator archive repair against the full compatible schema', () => { expect(JSON.parse(await readFile(artifacts.report, 'utf8')).repairsCompleted).toEqual( expect.arrayContaining([freeId, busyId]) ) + expect(await client`SELECT archived_at::text FROM project WHERE id = ${projectId}`).toEqual( + [{ archived_at: '2026-05-01 00:00:00' }] + ) await run('apply', artifacts) await run('verify', artifacts) } finally { @@ -179,6 +182,139 @@ describe('Operator archive repair against the full compatible schema', () => { 60000 ) + it('repairs an explicitly reviewed detached family without changing environments and replays a lost report', async () => { + const artifacts = { + manifest: join(directory, 'detach-manifest.json'), + report: join(directory, 'detach-report.json'), + } + try { + await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) + VALUES ('detach-owner','Owner','detach@fixture.test',true,now(),now())` + await client`INSERT INTO project (id,name,owner_id) VALUES ('detach-project','Keep context','detach-owner')` + await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id) + VALUES ('retained-root','Retained','detach-owner','detach-owner','detach-project'), + ('detached-root','Detached','detach-owner','detach-owner','detach-project')` + await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id,forked_from_workspace_id) + VALUES ('detached-child','Child','detach-owner','detach-owner','detach-project','detached-root')` + await expect(run('plan', artifacts)).rejects.toMatchObject({ code: 2 }) + const plan = JSON.parse(await readFile(artifacts.manifest, 'utf8')) + plan.groupings[0].decision = 'detach' + plan.groupings[0].detachRootId = 'detached-root' + await writeFile(artifacts.manifest, JSON.stringify(plan)) + const before = + await client`SELECT id,owner_id,organization_id,archived_at,forked_from_workspace_id FROM workspace ORDER BY id` + const holder = postgres(url.toString(), { max: 1 }) + try { + await holder.begin(async (tx) => { + await tx`SELECT pg_advisory_xact_lock(hashtextextended('project:detach-project',0))` + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + expect(await client`SELECT id FROM project WHERE owner_id='detach-owner'`).toHaveLength(1) + }) + } finally { + await holder.end() + } + await client`UPDATE workspace SET archived_at='2026-05-01' WHERE id='detached-child'` + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 1 }) + expect(await client`SELECT id FROM project WHERE owner_id='detach-owner'`).toHaveLength(1) + await client`UPDATE workspace SET archived_at=NULL WHERE id='detached-child'` + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + const destination = plan.groupings[0].destinationProjectId + expect( + await client`SELECT id FROM workspace WHERE project_id = ${destination} ORDER BY id` + ).toEqual([{ id: 'detached-child' }, { id: 'detached-root' }]) + expect(await client`SELECT id,name FROM project WHERE id = 'detach-project'`).toEqual([ + { id: 'detach-project', name: 'Keep context' }, + ]) + expect( + await client`SELECT id,owner_id,organization_id,archived_at,forked_from_workspace_id FROM workspace ORDER BY id` + ).toEqual(before) + await rm(artifacts.report) + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + expect(await client`SELECT id FROM project WHERE owner_id = 'detach-owner'`).toHaveLength(2) + } finally { + await client`DELETE FROM workspace WHERE owner_id = 'detach-owner'` + await client`DELETE FROM project WHERE owner_id = 'detach-owner'` + await client`DELETE FROM "user" WHERE id = 'detach-owner'` + } + }, 60000) + + it('keeps a Project active when another active environment still uses legacy membership', async () => { + const artifacts = { + manifest: join(directory, 'legacy-active-manifest.json'), + report: join(directory, 'legacy-active-report.json'), + } + try { + await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) + VALUES ('legacy-owner','Owner','legacy@fixture.test',true,now(),now())` + await client`INSERT INTO project (id,name,owner_id) VALUES ('legacy-project','Project','legacy-owner')` + await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id,archived_at) + VALUES ('legacy-archive','Archived','legacy-owner','legacy-owner','legacy-project','2026-05-01'), + ('legacy-live','Live','legacy-owner','legacy-owner',NULL,NULL)` + await client`INSERT INTO project_workspace (project_id,workspace_id) VALUES ('legacy-project','legacy-live'),('legacy-project','legacy-archive')` + await client`INSERT INTO workflow (id,name,user_id,workspace_id,last_synced,created_at,updated_at) + VALUES ('legacy-flow','Flow','legacy-owner','legacy-archive',now(),now(),now())` + await run('plan', artifacts) + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + expect(await client`SELECT archived_at FROM project WHERE id='legacy-project'`).toEqual([ + { archived_at: null }, + ]) + } finally { + await client`DELETE FROM workspace WHERE owner_id='legacy-owner'` + await client`DELETE FROM project WHERE id='legacy-project'` + await client`DELETE FROM "user" WHERE id='legacy-owner'` + } + }, 60000) + + it('completes provider cleanup despite a throwing local MCP subscriber', async () => { + const serverId = generateId() + try { + await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) + VALUES ('local-owner','Owner','local@fixture.test',true,now(),now())` + await client`INSERT INTO project (id,name,owner_id) VALUES ('local-project','Project','local-owner')` + await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id,archived_at) + VALUES ('local-env','Archived','local-owner','local-owner','local-project','2026-05-01')` + await client`INSERT INTO workflow (id,name,user_id,workspace_id,last_synced,created_at,updated_at) + VALUES ('local-flow','Flow','local-owner','local-env',now(),now(),now())` + await client`INSERT INTO workflow_mcp_server (id,workspace_id,created_by,name,is_public) + VALUES (${serverId},'local-env','local-owner','Server',true)` + await client`INSERT INTO workflow_mcp_tool (id,server_id,workflow_id,tool_name) + VALUES ('local-tool',${serverId},'local-flow','fixture')` + await execute( + 'bun', + [ + '--no-env-file', + '-e', + ` + import { repairArchivedProjectEnvironment } from '@/lib/projects/backfill-repair'; + import { mcpPubSub } from '@/lib/mcp/pubsub'; + import { db, dbReplica } from '@sim/db'; + import postgres from 'postgres'; + const client = postgres(process.env.MIGRATION_DATABASE_URL, {max:1}); + const unsubscribe = mcpPubSub.onWorkflowToolsChanged(() => { throw new Error('fixture subscriber failed'); }); + try { + await repairArchivedProjectEnvironment(client, {workspaceId:'local-env', archivedAt:'2026-05-01 00:00:00', workflowIds:['local-flow']}, 'local-subscriber'); + } finally { + unsubscribe(); mcpPubSub.dispose(); await client.end(); + await Promise.all([...new Set([db.$client,dbReplica.$client])].map(client=>client.end())); + } + `, + ], + { + cwd: new URL('../../../', import.meta.url), + env: { ...environment, REDIS_URL: '' }, + timeout: 45000, + } + ) + expect( + await client`SELECT completed_at IS NOT NULL AS complete FROM public.project_backfill_archive_repairs WHERE workspace_id = 'local-env'` + ).toEqual([{ complete: true }]) + } finally { + await client`DELETE FROM workspace WHERE id = 'local-env'` + await client`DELETE FROM project WHERE id = 'local-project'` + await client`DELETE FROM "user" WHERE id = 'local-owner'` + } + }, 60000) + it('commits the complete cascade, reports external failure and resumes cleanup from its original manifest', async () => { await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) VALUES ('owner','Owner','repair@fixture.test',true,now(),now())` await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,archived_at) VALUES ('env','Archived','owner','owner','2026-05-01')` @@ -278,4 +414,50 @@ describe('Operator archive repair against the full compatible schema', () => { ) expect(await client`SELECT project_id FROM project_workspace`).toHaveLength(0) }, 120000) + it('finishes the real migration entrypoint with exact reviewed grouping and retries deployment without the private file', async () => { + const artifacts = { + manifest: join(directory, 'entrypoint-manifest.json'), + report: join(directory, 'entrypoint-report.json'), + } + await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) + VALUES ('entry-owner','Owner','entry@fixture.test',true,now(),now())` + await client`INSERT INTO project (id,name,owner_id) VALUES ('entry-project','Keep shared context','entry-owner')` + await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id) + VALUES ('entry-a','A','entry-owner','entry-owner','entry-project'),('entry-b','B','entry-owner','entry-owner','entry-project')` + await expect(run('plan', artifacts)).rejects.toMatchObject({ code: 2 }) + const migrate = (reviewPath?: string) => + execute('bun', ['--no-env-file', 'scripts/migrate.ts'], { + cwd: new URL('../../../../../packages/db/', import.meta.url), + env: { ...environment, PROJECT_BACKFILL_REVIEW_PATH: reviewPath }, + timeout: 120000, + }) + await expect(migrate()).rejects.toMatchObject({ code: 1 }) + const reviewed = JSON.parse(await readFile(artifacts.manifest, 'utf8')) + reviewed.groupings[0].decision = 'retain' + await writeFile(artifacts.manifest, JSON.stringify(reviewed)) + await client`UPDATE workspace SET archived_at='2026-05-01' WHERE id='entry-b'` + await expect(migrate(artifacts.manifest)).rejects.toMatchObject({ code: 1 }) + expect( + await client`SELECT name FROM script_migrations WHERE name='0031_project_membership'` + ).toHaveLength(0) + expect( + await client`SELECT to_regclass('public.project_workspace') IS NOT NULL AS present` + ).toEqual([{ present: true }]) + await client`UPDATE workspace SET archived_at=NULL WHERE id='entry-b'` + await migrate(artifacts.manifest) + expect( + await client`SELECT name FROM script_migrations WHERE name='0031_project_membership'` + ).toHaveLength(1) + expect(await client`SELECT to_regclass('public.project_workspace') AS connector`).toEqual([ + { connector: null }, + ]) + expect( + await client`SELECT id,project_id FROM workspace WHERE owner_id='entry-owner' ORDER BY id` + ).toEqual([ + { id: 'entry-a', project_id: 'entry-project' }, + { id: 'entry-b', project_id: 'entry-project' }, + ]) + await rm(artifacts.manifest) + await migrate() + }, 120000) }) diff --git a/apps/sim/lib/projects/backfill-repair.ts b/apps/sim/lib/projects/backfill-repair.ts index f6d62e6ca87..1e5e5a36ba4 100644 --- a/apps/sim/lib/projects/backfill-repair.ts +++ b/apps/sim/lib/projects/backfill-repair.ts @@ -2,6 +2,9 @@ import { type ProjectArchiveRepair, ProjectBackfillBusy, ProjectBackfillConflict, + type ProjectBackfillWorkspace, + type ProjectGroupingReview, + projectGroupingEvidence, } from '@sim/db/maintenance/project-backfill' import { completeProjectArchiveRepair, @@ -12,6 +15,7 @@ import { and, eq, inArray, isNull, sql } from 'drizzle-orm' import { drizzle } from 'drizzle-orm/postgres-js' import type { Sql } from 'postgres' import { tryAcquireAdvisoryXactLocks } from '@/lib/db/advisory-locks' +import { archiveProjectWithLastEnvironment, splitForkProject } from '@/lib/projects/membership' import { archiveEnvironmentInTransaction, type EnvironmentArchiveEffects, @@ -50,11 +54,18 @@ export async function repairArchivedProjectEnvironment( ) throw new ProjectBackfillBusy('Archive repair Project is busy') const [current] = await tx - .select({ archivedAt: sql`${schema.workspace.archivedAt}::text` }) + .select({ + projectId: schema.workspace.projectId, + archivedAt: sql`${schema.workspace.archivedAt}::text`, + }) .from(schema.workspace) .where(eq(schema.workspace.id, repair.workspaceId)) .for('no key update', { noWait: true }) - if (!current || current.archivedAt !== repair.archivedAt) + if ( + !current || + current.archivedAt !== repair.archivedAt || + current.projectId !== membership?.projectId + ) throw new ProjectBackfillConflict('Reviewed archive changed; rediscover') const active = await tx .select({ id: schema.workflow.id }) @@ -76,7 +87,7 @@ export async function repairArchivedProjectEnvironment( .limit(1) if (moved.length) throw new ProjectBackfillConflict('Reviewed workflow moved; rediscover') const recorded = - await tx.execute(sql`INSERT INTO project_backfill_archive_repairs (workspace_id, repair) + await tx.execute(sql`INSERT INTO public.project_backfill_archive_repairs (workspace_id, repair) VALUES (${repair.workspaceId}, ${JSON.stringify(repair)}::text::jsonb) ON CONFLICT (workspace_id) DO UPDATE SET repair = EXCLUDED.repair, completed_at = NULL WHERE project_backfill_archive_repairs.repair = EXCLUDED.repair @@ -86,11 +97,25 @@ export async function repairArchivedProjectEnvironment( throw new ProjectBackfillConflict( 'Finish the pending repair with its original manifest before replacing it' ) - await archiveEnvironmentInTransaction( - tx, - repair.workspaceId, - new Date(`${repair.archivedAt.replace(' ', 'T')}Z`) - ) + const archivedAt = new Date(`${repair.archivedAt.replace(' ', 'T')}Z`) + if (membership?.projectId) { + const [legacy] = await tx.execute<{ present: boolean }>( + sql`SELECT to_regclass('public.project_workspace') IS NOT NULL AS present` + ) + const activeLegacy = legacy?.present + ? await tx.execute(sql` + SELECT 1 FROM project_workspace pw JOIN workspace w ON w.id = pw.workspace_id + WHERE pw.project_id = ${membership.projectId} AND w.project_id IS NULL AND w.archived_at IS NULL LIMIT 1`) + : [] + if (!activeLegacy.length) + await archiveProjectWithLastEnvironment( + tx, + membership.projectId, + repair.workspaceId, + archivedAt + ) + } + await archiveEnvironmentInTransaction(tx, repair.workspaceId, archivedAt) const tools = await tx .select({ workflowId: schema.workflowMcpTool.workflowId, @@ -115,3 +140,86 @@ export async function repairArchivedProjectEnvironment( await finishEnvironmentArchive(effects, requestId, { strictExternalCleanup: true }) await completeProjectArchiveRepair(client, repair) } + +/** Repairs only an operator-named detached root, preserving the shared split policy and a replayable destination ID. */ +export async function repairProjectGrouping( + client: Sql, + review: ProjectGroupingReview +): Promise { + const rootId = review.detachRootId + if ( + review.decision !== 'detach' || + !rootId || + !review.roots.includes(rootId) || + review.destinationProjectId === review.projectId || + review.members.length > 1000 || + review.members.some((row) => row.projectId !== review.projectId) || + projectGroupingEvidence(review.members) !== review.evidence + ) + throw new ProjectBackfillConflict( + 'Detach requires an exact reviewed, fully assigned Project; rediscover after apply' + ) + const expected = new Map(review.members.map((row) => [row.id, row])) + const root = expected.get(rootId) + if (!root || root.parentId !== null) + throw new ProjectBackfillConflict('Reviewed detached root is not independent') + const targetIds = new Set([rootId]) + for (let size = 0; size !== targetIds.size; ) { + size = targetIds.size + for (const row of review.members) + if (row.parentId && targetIds.has(row.parentId)) targetIds.add(row.id) + } + const target = review.members.filter((row) => targetIds.has(row.id)) + const connection = drizzle({ client, schema }) + await connection.transaction(async (tx) => { + await tx.execute(sql`SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL + THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true)`) + await tx.execute(sql`SET LOCAL statement_timeout = '3s'`) + await tx.execute(sql`SET LOCAL lock_timeout = '250ms'`) + const keys = [ + `project:${review.projectId}`, + `project:${review.destinationProjectId}`, + ...review.roots.map((id) => `fork-lineage:${id}`), + ...review.members.map((row) => `project-backfill:${row.id}`), + ...(root.organizationId ? [`permission_group:${root.organizationId}`] : []), + ] + if (!(await tryAcquireAdvisoryXactLocks(tx, 'project_backfill', keys))) + throw new ProjectBackfillBusy('Reviewed Project grouping is busy') + const current = await tx.execute(sql` + SELECT id, forked_from_workspace_id AS "parentId", owner_id AS "ownerId", + organization_id AS "organizationId", archived_at::text AS "archivedAt", project_id AS "projectId", NULL::text AS "legacyProjectId" + FROM workspace WHERE project_id IN (${review.projectId},${review.destinationProjectId}) + ORDER BY id COLLATE "C" LIMIT 1001 FOR NO KEY UPDATE NOWAIT`) + if (current.length > 1000) + throw new ProjectBackfillConflict('Project grouping exceeds repair limit') + const destination = current.filter((row) => row.projectId === review.destinationProjectId) + if (destination.length) { + if ( + projectGroupingEvidence( + destination.map((row) => ({ ...row, projectId: review.projectId })) + ) !== projectGroupingEvidence(target) + ) + throw new ProjectBackfillConflict('Reviewed detach destination changed; rediscover') + return + } + if (projectGroupingEvidence([...current]) !== review.evidence) + throw new ProjectBackfillConflict('Reviewed Project grouping changed; rediscover') + const [source] = await tx + .select({ ownerId: schema.project.ownerId, organizationId: schema.project.organizationId }) + .from(schema.project) + .where(eq(schema.project.id, review.projectId)) + .for('no key update', { noWait: true }) + if ( + !source || + current.some( + (row) => + row.organizationId !== source.organizationId || + (source.organizationId === null && row.ownerId !== source.ownerId) + ) + ) + throw new ProjectBackfillConflict( + 'Reviewed Project ownership or scope requires reconciliation before detach' + ) + await splitForkProject(tx, rootId, review.destinationProjectId) + }) +} diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index f3b35bfc92a..14c1daf16f2 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -204,7 +204,11 @@ export async function archiveProjectWithLastEnvironment( * Moves the detached subtree into a new Project and returns its id. Called before * clearing the edge, under the existing lineage lock. */ -export async function splitForkProject(tx: DbTransaction, workspaceId: string): Promise { +export async function splitForkProject( + tx: DbTransaction, + workspaceId: string, + newProjectId = generateId() +): Promise { const owner = await lockWorkspaceProject(tx, workspaceId) if (!owner) throw new ProjectConflictError('Workspace no longer exists') if (owner.archivedAt) throw new ProjectConflictError('Cannot disconnect an archived Project') @@ -238,7 +242,7 @@ export async function splitForkProject(tx: DbTransaction, workspaceId: string): throw new ProjectConflictError( 'Disconnecting would remove the last active environment from this Project' ) - const id = generateId() + const id = newProjectId await tx.insert(project).values({ id, name: generatedProjectName(root.name), diff --git a/apps/sim/lib/workflows/lifecycle.ts b/apps/sim/lib/workflows/lifecycle.ts index db50c33047b..183f7ee0c2f 100644 --- a/apps/sim/lib/workflows/lifecycle.ts +++ b/apps/sim/lib/workflows/lifecycle.ts @@ -420,7 +420,14 @@ export async function finishWorkflowArchive( if (workspaceId && mcpPubSub) { for (const serverId of new Set(serverIds)) { - mcpPubSub.publishWorkflowToolsChanged({ serverId, workspaceId }) + try { + mcpPubSub.publishWorkflowToolsChanged({ serverId, workspaceId }) + } catch (error) { + logger.warn( + `[${options.requestId}] MCP tools-changed publish failed for server ${serverId}`, + { error } + ) + } } } } diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index 7a38272dde5..02101e7c145 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -10,6 +10,7 @@ import { ProjectBackfillBusy, ProjectBackfillConflict, projectBackfillDatabaseId, + readProjectGroupingReviews, verifyProjectBackfill, } from '@sim/db/maintenance/project-backfill' import { withUtcTimestamps } from '@sim/db/timestamps' @@ -41,7 +42,15 @@ const manifestSchema = z databaseId: text, createdAt: text, families: z - .array(z.object({ rootId: text, members: z.array(member).min(1).max(1000) }).strict()) + .array( + z + .object({ + rootId: text, + members: z.array(member).min(1).max(1000), + projectMembers: z.array(member).min(1).max(1000).optional(), + }) + .strict() + ) .max(250000), repairs: z .array( @@ -54,6 +63,22 @@ const manifestSchema = z .strict() ) .max(250000), + groupings: z + .array( + z + .object({ + projectId: text, + evidence: text, + members: z.array(member).min(1).max(1000), + destinationProjectId: text, + roots: z.array(text).max(1000), + decision: z.enum(['pending', 'retain', 'detach']), + detachRootId: text.optional(), + }) + .strict() + ) + .max(250000) + .default([]), conflicts: z.array(z.object({ id: text, reason: text }).strict()).max(250000), }) .strict() @@ -67,6 +92,7 @@ const progressSchema = z nextIndex: z.number().int().min(0), deferred: z.array(z.number().int().min(0)), repairsCompleted: z.array(text), + groupingsCompleted: z.array(text).default([]), assigned: z.number().int().min(0), projectsCreated: z.number().int().min(0), alreadyAssigned: z.number().int().min(0), @@ -118,6 +144,7 @@ async function main() { allowPositionals: true, options: { manifest: { type: 'string' }, + review: { type: 'string' }, report: { type: 'string' }, 'batch-size': { type: 'string' }, 'max-batches': { type: 'string' }, @@ -131,9 +158,16 @@ async function main() { process.stdout.write(`Operator-controlled Project inspection and exceptional archive repair. Routine assignment and enforcement run through db:migrate.\n From apps/sim: bun --no-env-file scripts/backfill-projects.ts [options]\n -plan Read-only discovery; atomically writes a new --manifest PATH (never overwrites).\nrepair Repair only archived environments listed in --manifest; requires --report PATH.\napply Optional maintenance: assign reviewed families from --manifest; requires --report PATH.\nverify Validate the database and repair completion; requires --manifest and --report.\nstatus Read --report; does not connect or resume.\n +plan Read-only discovery; writes a new --manifest PATH. Optional --review PATH carries reviewed grouping decisions.\nrepair Repair reviewed archives and explicitly selected detached families; requires --report PATH.\napply Optional maintenance: assign reviewed families from --manifest; requires --report PATH.\nverify Validate the database and repair completion; requires --manifest and --report.\nstatus Read --report; does not connect or resume.\n Writes require MIGRATION_DATABASE_URL pointing directly to the primary and --ack-release-drained.\nDeploy #8830 and verify old servers/workers drained first. Keep the reviewed manifest and report\nin durable private job storage. Review conflicts and repairs before repair/apply. Repair requires\nthe app runtime environment (DATABASE_URL must target the same database) for provider cleanup.\n -Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (1–3600), --pause-ms 100 (1–60000).\nA budget limits scheduling. Transactions use 3s statement limits and a 5s total limit on PG17+,\notherwise a 5s idle-transaction limit on PG16. Reuse the same report\nto resume; lost reports can be recreated safely from the same manifest. Changed families need a\nnew plan/report. Resolve conflicts and complete archive/provider cleanup before #8590. +Defaults: --batch-size 50 (1–50), --max-batches 100 (1–10000), --seconds 60 (1–3600), --pause-ms 100 (1–60000).\nA budget limits scheduling. Transactions use 3s statement limits and a 5s total limit on PG17+,\notherwise a 5s idle-transaction limit on PG16. Reuse the same report\nto resume; lost reports can be recreated safely from the same manifest. Changed families need a\nnew plan/report. Review groupings explicitly: retain a legitimate group, or select one detachRootId +and keep its generated destinationProjectId for replay. After detach, rediscover. Resolve all other +conflicts and complete archive/provider cleanup before #8590. The registered runner can read exact retained-group decisions from PROJECT_BACKFILL_REVIEW_PATH. +After an interrupted deployment, run packages/db/scripts/migrate.ts from the exact release checkout +on an authorized operator host with that private manifest. Direct invocation does not run the GHA +AWS preflight: freshly verify the compatible deployed release, old app/worker drainage and release +coordination first. Prior failed CI is not continuing proof if deployment state changed. Once the +runner succeeds and writes its normal receipt, the ordinary deployment retry needs no local file. The registered migration backfills remaining assignments, verifies, then enforces.\nExit: 0 complete, 2 running/paused/incomplete, 1 failure. Use db:migrate for routine backfill and enforcement after writer drainage.\n`) return } @@ -143,6 +177,7 @@ The registered migration backfills remaining assignments, verifies, then enforce !['plan', 'repair', 'apply', 'verify', 'status'].includes(command) ) throw new Error('Unknown command; use --help') + if (values.review && command !== 'plan') throw new Error('--review is only supported by plan') const manifestPath = values.manifest ? resolve(values.manifest) : null const reportPath = values.report ? resolve(values.report) : null if (manifestPath && manifestPath === reportPath) @@ -209,7 +244,10 @@ The registered migration backfills remaining assignments, verifies, then enforce lockPid = lock.pid if (command === 'plan') { if (!manifestPath) throw new Error('plan requires --manifest') - const manifest = await discoverProjectBackfill(sql, databaseId) + const reviews = values.review + ? await readProjectGroupingReviews(resolve(values.review), databaseId) + : [] + const manifest = await discoverProjectBackfill(sql, databaseId, reviews) await assertLock() await writeJson(manifestPath, manifest, false) logger.info('Reviewable Project plan written', { @@ -273,6 +311,7 @@ The registered migration backfills remaining assignments, verifies, then enforce nextIndex: 0, deferred: [], repairsCompleted: [], + groupingsCompleted: [], assigned: 0, alreadyAssigned: 0, projectsCreated: 0, @@ -332,7 +371,9 @@ The registered migration backfills remaining assignments, verifies, then enforce 'Repair requires DATABASE_URL and MIGRATION_DATABASE_URL to identify the same database' ) repairRuntimeLoaded = true - const { repairArchivedProjectEnvironment } = await import('@/lib/projects/backfill-repair') + const { repairArchivedProjectEnvironment, repairProjectGrouping } = await import( + '@/lib/projects/backfill-repair' + ) for (const repair of manifest.repairs) { if (completedRepairs.has(repair.workspaceId)) continue if (stop || attempts >= maxBatches || performance.now() - started >= seconds * 1000) break @@ -359,6 +400,30 @@ The registered migration backfills remaining assignments, verifies, then enforce } await sleep(pauseMs) } + for (const grouping of manifest.groupings) { + if ( + grouping.decision !== 'detach' || + progress.groupingsCompleted.includes(grouping.projectId) + ) + continue + if (stop || attempts >= maxBatches || performance.now() - started >= seconds * 1000) break + await assertLock() + attempts++ + try { + await repairProjectGrouping(sql, grouping) + await assertLock() + progress.groupingsCompleted.push(grouping.projectId) + await checkpoint() + } catch (error) { + if (!(error instanceof ProjectBackfillBusy) && !getTransientDatabaseFailure(error)) + throw error + await assertLock() + logger.warn('Reviewed grouping repair deferred', { + projectId: grouping.projectId, + error: describeError(error), + }) + } + } } else { for (let position = 0; position < pending.length; ) { if (stop || attempts >= maxBatches || performance.now() - started >= seconds * 1000) break diff --git a/packages/db/maintenance/project-backfill.test.ts b/packages/db/maintenance/project-backfill.test.ts new file mode 100644 index 00000000000..bf7be552419 --- /dev/null +++ b/packages/db/maintenance/project-backfill.test.ts @@ -0,0 +1,16 @@ +import { projectBackfillDatabaseId } from '@sim/db/maintenance/project-backfill' +import { describe, expect, it } from 'vitest' + +describe('Project manifest database binding', () => { + it('distinguishes socket routing and query overrides without binding credentials', () => { + expect(projectBackfillDatabaseId('postgres:///prod?host=/var/run/postgresql')).not.toBe( + projectBackfillDatabaseId('postgres:///prod?host=/tmp/other') + ) + expect(projectBackfillDatabaseId('postgres://localhost/prod?port=5433')).not.toBe( + projectBackfillDatabaseId('postgres://localhost/prod?port=5434') + ) + expect(projectBackfillDatabaseId('postgres://alice:one@localhost/prod')).toBe( + projectBackfillDatabaseId('postgres://bob:two@localhost:5432/prod') + ) + }) +}) diff --git a/packages/db/maintenance/project-backfill.ts b/packages/db/maintenance/project-backfill.ts index 1e726e4fa1e..beb7c3f591a 100644 --- a/packages/db/maintenance/project-backfill.ts +++ b/packages/db/maintenance/project-backfill.ts @@ -1,7 +1,9 @@ import { createHash } from 'node:crypto' +import { readFile, stat } from 'node:fs/promises' import { countPendingProjectArchiveRepairs } from '@sim/db/maintenance/project-repairs' import { createLogger } from '@sim/logger' import { generateId } from '@sim/utils/id' +import { isRecordLike } from '@sim/utils/object' import { compareStrings, truncateAtCodePoint } from '@sim/utils/string' import type { Sql, TransactionSql } from 'postgres' @@ -9,7 +11,7 @@ const logger = createLogger('ProjectBackfill') const MAX_WORKSPACES = 250_000 const MAX_FAMILY = 1_000 -interface ProjectBackfillWorkspace { +export type ProjectBackfillWorkspace = { id: string parentId: string | null ownerId: string @@ -22,6 +24,7 @@ interface ProjectBackfillWorkspace { interface ProjectBackfillFamily { rootId: string members: ProjectBackfillWorkspace[] + projectMembers?: ProjectBackfillWorkspace[] } export interface ProjectArchiveRepair { @@ -30,12 +33,23 @@ export interface ProjectArchiveRepair { workflowIds: string[] } +export interface ProjectGroupingReview { + projectId: string + evidence: string + members: ProjectBackfillWorkspace[] + destinationProjectId: string + roots: string[] + decision: 'pending' | 'retain' | 'detach' + detachRootId?: string +} + interface ProjectBackfillManifest { version: 1 databaseId: string createdAt: string families: ProjectBackfillFamily[] repairs: ProjectArchiveRepair[] + groupings: ProjectGroupingReview[] conflicts: { id: string; reason: string }[] } @@ -51,10 +65,107 @@ export class ProjectBackfillBusy extends Error {} export function projectBackfillDatabaseId(url: string): string { const target = new URL(url) return createHash('sha256') - .update(`${target.hostname.toLowerCase()}:${target.port || '5432'}${target.pathname}`) + .update( + JSON.stringify({ + host: target.hostname.toLowerCase() || process.env.PGHOST || 'localhost', + port: target.port || process.env.PGPORT || '5432', + database: target.pathname, + routing: [ + 'host', + 'hostaddr', + 'hostname', + 'port', + 'database', + 'dbname', + 'service', + 'servicefile', + 'target_session_attrs', + ].map((key) => [key, target.searchParams.getAll(key)]), + }) + ) .digest('hex') } +/** Binds an explicit grouping decision to membership, lineage, lifecycle and ownership evidence. */ +export function projectGroupingEvidence(rows: ProjectBackfillWorkspace[]): string { + return createHash('sha256') + .update( + JSON.stringify( + [...rows] + .sort((a, b) => compareStrings(a.id, b.id)) + .map((row) => ({ + id: row.id, + parentId: row.parentId, + ownerId: row.ownerId, + organizationId: row.organizationId, + archivedAt: row.archivedAt, + projectId: effectiveProjectId(row), + })) + ) + ) + .digest('hex') +} + +function isProjectBackfillWorkspace(value: unknown): value is ProjectBackfillWorkspace { + return ( + isRecordLike(value) && + typeof value.id === 'string' && + typeof value.ownerId === 'string' && + ['parentId', 'organizationId', 'archivedAt', 'projectId', 'legacyProjectId'].every( + (key) => value[key] === null || typeof value[key] === 'string' + ) + ) +} + +/** Exceptional operator review stays in the existing private manifest, never the retiring connector. */ +export async function readProjectGroupingReviews( + path: string, + databaseId: string +): Promise { + if ((await stat(path)).size > 128 * 1024 * 1024) + throw new Error('Project review exceeds artifact size limit') + const value: unknown = JSON.parse(await readFile(path, 'utf8')) + if ( + !isRecordLike(value) || + value.version !== 1 || + value.databaseId !== databaseId || + !Array.isArray(value.groupings) + ) + throw new Error('Project grouping review does not match this database') + const reviews: ProjectGroupingReview[] = [] + for (const item of value.groupings) { + if ( + !isRecordLike(item) || + !Array.isArray(item.members) || + item.members.length > MAX_FAMILY || + !item.members.every(isProjectBackfillWorkspace) || + typeof item.destinationProjectId !== 'string' || + typeof item.projectId !== 'string' || + typeof item.evidence !== 'string' || + !Array.isArray(item.roots) || + item.roots.length > MAX_FAMILY || + !item.roots.every((root): root is string => typeof root === 'string') || + !['pending', 'retain', 'detach'].includes(String(item.decision)) || + (item.detachRootId !== undefined && typeof item.detachRootId !== 'string') + ) + throw new Error('Invalid Project grouping review') + if (projectGroupingEvidence(item.members) !== item.evidence) + throw new Error('Project grouping evidence does not match the reviewed members') + if (reviews.some((review) => review.projectId === item.projectId)) + throw new Error('Duplicate Project grouping review') + reviews.push({ + projectId: item.projectId, + evidence: item.evidence, + members: item.members, + destinationProjectId: item.destinationProjectId, + roots: item.roots, + decision: item.decision as ProjectGroupingReview['decision'], + ...(typeof item.detachRootId === 'string' ? { detachRootId: item.detachRootId } : {}), + }) + } + return reviews +} + /** Checks physical prerequisites without trusting a local migration journal or a CLI flag. */ export async function assertProjectBackfillDatabase(sql: Sql, write: boolean): Promise { const [state] = await sql` @@ -78,8 +189,7 @@ async function hasLegacyMemberships(tx: TransactionSql): Promise { function legacyAssignment(tx: TransactionSql, legacy: boolean) { return legacy - ? tx`CASE WHEN w.project_id IS NULL THEN - (SELECT pw.project_id FROM project_workspace pw WHERE pw.workspace_id = w.id) END` + ? tx`(SELECT pw.project_id FROM project_workspace pw WHERE pw.workspace_id = w.id)` : tx`NULL::text` } @@ -87,26 +197,19 @@ function effectiveProjectId(row: ProjectBackfillWorkspace): string | null { return row.projectId ?? row.legacyProjectId } -function projectEnvironments(tx: TransactionSql, legacy: boolean, projectId: string) { - const columns = tx`SELECT id, archived_at FROM workspace WHERE project_id = ${projectId}` - return legacy - ? tx`${columns} UNION ALL - SELECT w.id, w.archived_at FROM project_workspace pw - JOIN workspace w ON w.id = pw.workspace_id - WHERE pw.project_id = ${projectId} AND w.project_id IS NULL` - : columns -} - /** Bounded discovery materializes only identity/scope metadata, never workflow content. */ export async function discoverProjectBackfill( sql: Sql, - databaseId: string + databaseId: string, + reviews: ProjectGroupingReview[] = [] ): Promise { const rows: ProjectBackfillWorkspace[] = [] + let legacyPresent = false let after = '' for (;;) { const page = await sql.begin(async (tx) => { const legacy = await hasLegacyMemberships(tx) + legacyPresent ||= legacy return tx` SELECT w.id, w.forked_from_workspace_id AS "parentId", w.owner_id AS "ownerId", w.organization_id AS "organizationId", w.archived_at::text AS "archivedAt", @@ -127,6 +230,7 @@ export async function discoverProjectBackfill( createdAt: new Date().toISOString(), families: [], repairs: [], + groupings: [], conflicts: [], } const byId = new Map(rows.map((row) => [row.id, row])) @@ -158,16 +262,118 @@ export async function discoverProjectBackfill( if (group) group.push(row) else groups.set(root, [row]) } - for (const [rootId, members] of groups) { + const projects = await sql< + { id: string; ownerId: string; organizationId: string | null; archivedAt: string | null }[] + >` + SELECT id, owner_id AS "ownerId", organization_id AS "organizationId", archived_at::text AS "archivedAt" FROM project LIMIT 250001 + ` + if (projects.length > MAX_WORKSPACES) + throw new Error('Project discovery exceeds the Project limit') + const affectedProjects = new Set() + const projectMembers = new Map() + for (const row of rows) { + const id = effectiveProjectId(row) + if (!id) continue + const members = projectMembers.get(id) ?? [] + members.push(row) + projectMembers.set(id, members) + } + for (const project of projects) { + const members = projectMembers.get(project.id) ?? [] + if (!members.length) { + manifest.conflicts.push({ + id: project.id, + reason: 'Empty Project requires explicit dependency review and operator remediation', + }) + continue + } + const roots = new Set(members.map((row) => rootById.get(row.id))) + if ( + roots.size > 1 && + members.some((row) => row.parentId === null && row.legacyProjectId !== project.id) + ) { + if (legacyPresent) { + const evidence = projectGroupingEvidence(members) + const reviewed = reviews.find( + (review) => review.projectId === project.id && review.evidence === evidence + ) + const decision = reviewed?.decision ?? 'pending' + if (members.length > MAX_FAMILY) { + manifest.conflicts.push({ + id: project.id, + reason: 'Oversized ambiguous Project grouping requires operator remediation', + }) + continue + } + manifest.groupings.push({ + projectId: project.id, + evidence, + members, + destinationProjectId: reviewed?.destinationProjectId ?? generateId(), + roots: [...roots] + .filter((root): root is string => typeof root === 'string') + .sort(compareStrings), + decision, + ...(reviewed?.detachRootId ? { detachRootId: reviewed.detachRootId } : {}), + }) + if (decision !== 'retain') + manifest.conflicts.push({ + id: project.id, + reason: + 'Ambiguous multi-root Project grouping; review this manifest grouping or repair the known detached family before rediscovery', + }) + } + } + if ( + new Set(members.map((row) => row.organizationId)).size !== 1 || + (!members[0].organizationId && members.some((row) => row.ownerId !== project.ownerId)) + ) { + affectedProjects.add(project.id) + } if ( - members.every((row) => row.projectId !== null) && - new Set(members.map((row) => row.projectId)).size === 1 + members.some((row) => row.organizationId !== project.organizationId) || + (project.archivedAt === null) !== members.some((row) => row.archivedAt === null) ) - continue + affectedProjects.add(project.id) + } + const plannedProjectMembers = new Map( + [...projectMembers].map(([id, members]) => [id, [...members]]) + ) + for (const members of groups.values()) { + const projectIds = [ + ...new Set(members.map(effectiveProjectId).filter((id): id is string => id !== null)), + ] + if (projectIds.length !== 1) continue + const planned = plannedProjectMembers.get(projectIds[0]) + if (planned) planned.push(...members.filter((row) => effectiveProjectId(row) === null)) + } + const scheduledMetadata = new Set() + for (const [rootId, members] of groups) { const family = { rootId, members } try { + const complete = + members.every((row) => row.projectId !== null) && + new Set(members.map((row) => row.projectId)).size === 1 + validateFamily(family, complete) + if ( + complete && + !members.some( + (row) => + row.projectId && + affectedProjects.has(row.projectId) && + !scheduledMetadata.has(row.projectId) + ) + ) + continue validateFamily(family) - manifest.families.push(family) + const projectId = members.map(effectiveProjectId).find((id) => id !== null) + if (projectId && (plannedProjectMembers.get(projectId)?.length ?? 0) > MAX_FAMILY) + throw new ProjectBackfillConflict('Oversized Project repair requires operator remediation') + if (projectId) scheduledMetadata.add(projectId) + manifest.families.push({ + ...family, + ...(projectId ? { projectMembers: plannedProjectMembers.get(projectId) } : {}), + }) } catch (error) { if (!(error instanceof ProjectBackfillConflict)) throw error manifest.conflicts.push({ id: rootId, reason: error.message }) @@ -211,23 +417,25 @@ export async function discoverProjectBackfill( return manifest } -function validateFamily(family: ProjectBackfillFamily): void { +function validateFamily(family: ProjectBackfillFamily, complete = false): void { const root = family.members.find((row) => row.id === family.rootId) - if (!root || root.parentId || family.members.length > MAX_FAMILY) + if (!root || root.parentId || (!complete && family.members.length > MAX_FAMILY)) throw new ProjectBackfillConflict('Invalid or oversized fork family') const members = new Map(family.members.map((row) => [row.id, row])) if (members.size !== family.members.length) throw new ProjectBackfillConflict('Duplicate family member') + const connected = new Set([root.id]) for (const row of family.members) { let current = row const visited = new Set() - while (current.id !== root.id) { + while (!connected.has(current.id)) { if (visited.has(current.id)) throw new ProjectBackfillConflict('Cyclic family') visited.add(current.id) const parent = current.parentId ? members.get(current.parentId) : undefined if (!parent) throw new ProjectBackfillConflict('Disconnected family member') current = parent } + for (const id of visited) connected.add(id) } if (family.members.some((row) => row.organizationId !== root.organizationId)) throw new ProjectBackfillConflict('Fork family spans organizations') @@ -366,27 +574,75 @@ export async function assignProjectBackfillBatch(sql: Sql, families: ProjectBack if (projectId) { const [existing] = await tx`SELECT owner_id, organization_id, archived_at::text FROM project WHERE id = ${projectId} FOR NO KEY UPDATE NOWAIT` - const familyIds = members.map((row) => row.id) - const [membership] = await tx` - SELECT EXISTS (SELECT 1 FROM (${projectEnvironments(tx, legacy, projectId)}) environments - WHERE archived_at IS NULL) AS active, - EXISTS (SELECT 1 FROM (${projectEnvironments(tx, legacy, projectId)}) environments - WHERE NOT id = ANY(${familyIds})) AS outside + const reviewedIds = family.projectMembers?.map((row) => row.id) ?? [] + const evidenceRows = await tx` + SELECT w.id, w.owner_id AS "ownerId", w.organization_id AS "organizationId", w.archived_at::text AS "archivedAt", + w.forked_from_workspace_id AS "parentId", w.project_id AS "projectId", ${legacyAssignment(tx, legacy)} AS "legacyProjectId" + FROM workspace w WHERE coalesce(w.project_id, ${legacyAssignment(tx, legacy)}) = ${projectId} + OR w.id = ANY(${reviewedIds}) + ORDER BY w.id COLLATE "C" LIMIT 1001 FOR NO KEY UPDATE NOWAIT ` - const hasActiveEnvironment = archiveAt === null || membership.active - if ( - !existing || - existing.organization_id !== root.organizationId || - (existing.archived_at === null) !== hasActiveEnvironment || - (!root.organizationId && existing.owner_id !== root.ownerId) - ) + const environments = evidenceRows.filter((row) => effectiveProjectId(row) === projectId) + if (!existing || evidenceRows.length > MAX_FAMILY) throw new ProjectBackfillConflict( - 'Existing Project has incompatible ownership, scope or archive state' + 'Missing Project or oversized Project repair; operator remediation required' + ) + if (family.projectMembers) { + const reviewed = new Map( + [ + ...family.projectMembers, + ...family.members.filter((row) => effectiveProjectId(row) === null), + ].map((row) => [row.id, row]) ) - if (members.some((row) => effectiveProjectId(row) === null) && membership.outside) + const currentIds = new Set(evidenceRows.map((row) => row.id)) + if ( + family.projectMembers.some((row) => !currentIds.has(row.id)) || + evidenceRows.some((row) => { + const expected = reviewed.get(row.id) + return ( + !expected || + row.parentId !== expected.parentId || + row.ownerId !== expected.ownerId || + row.organizationId !== expected.organizationId || + row.archivedAt !== expected.archivedAt || + (effectiveProjectId(expected) !== null + ? effectiveProjectId(row) !== effectiveProjectId(expected) + : effectiveProjectId(row) !== null && effectiveProjectId(row) !== projectId) + ) + }) + ) + throw new ProjectBackfillConflict('Reviewed Project membership changed; rediscover') + } + const allMembers = [ + ...environments, + ...members.filter((row) => effectiveProjectId(row) === null), + ] + if ( + allMembers.some((row) => row.organizationId !== root.organizationId) || + (!root.organizationId && allMembers.some((row) => row.ownerId !== existing.owner_id)) || + (existing.organization_id !== root.organizationId && + allMembers.some((row) => row.ownerId !== existing.owner_id)) + ) throw new ProjectBackfillConflict( - 'Partial assignment references a Project outside the reviewed family' + 'Existing Project has ambiguous ownership or scope; operator remediation required' ) + const projectArchiveAt = allMembers.some((row) => row.archivedAt === null) + ? null + : (allMembers + .map((row) => row.archivedAt ?? '') + .sort(compareStrings) + .at(-1) ?? null) + if ( + existing.organization_id !== root.organizationId || + (existing.archived_at === null) !== (projectArchiveAt === null) + ) { + if (!family.projectMembers) + throw new ProjectBackfillConflict( + 'Project repair needs fresh Project-wide evidence; rediscover' + ) + await tx`UPDATE project SET organization_id = ${root.organizationId}, + archived_at = ${projectArchiveAt}::text::timestamp, updated_at = now() WHERE id = ${projectId}` + } } else { projectId = generateId() inserts.push({ diff --git a/packages/db/maintenance/project-repairs.ts b/packages/db/maintenance/project-repairs.ts index 60897d9f5c4..80262788134 100644 --- a/packages/db/maintenance/project-repairs.ts +++ b/packages/db/maintenance/project-repairs.ts @@ -3,7 +3,7 @@ import type { Sql } from 'postgres' /** Runner-managed recovery state, like script_migrations; never an application ownership table. */ export async function ensureProjectArchiveRepairJournal(sql: Sql): Promise { - await sql`CREATE TABLE IF NOT EXISTS project_backfill_archive_repairs ( + await sql`CREATE TABLE IF NOT EXISTS public.project_backfill_archive_repairs ( workspace_id text PRIMARY KEY, repair jsonb NOT NULL, completed_at timestamptz @@ -15,7 +15,7 @@ export async function completeProjectArchiveRepair( sql: Sql, repair: ProjectArchiveRepair ): Promise { - const rows = await sql`UPDATE project_backfill_archive_repairs SET completed_at = now() + const rows = await sql`UPDATE public.project_backfill_archive_repairs SET completed_at = now() WHERE workspace_id = ${repair.workspaceId} AND repair = ${JSON.stringify(repair)}::text::jsonb RETURNING workspace_id` if (!rows.length) throw new Error('Archive repair changed before cleanup completed') @@ -27,6 +27,6 @@ export async function countPendingProjectArchiveRepairs(sql: Sql): Promise count !== 0)) { diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 4f0bb7c35d5..5206b248579 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -7,6 +7,7 @@ import { promisify } from 'node:util' import { assignProjectBackfillBatch, discoverProjectBackfill, + type ProjectGroupingReview, projectBackfillDatabaseId, verifyProjectBackfill, } from '@sim/db/maintenance/project-backfill' @@ -20,7 +21,7 @@ import { generateId } from '@sim/utils/id' import { drizzle } from 'drizzle-orm/postgres-js' import { migrate } from 'drizzle-orm/postgres-js/migrator' import postgres, { type Sql } from 'postgres' -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' const migration = await readFile( new URL('../maintenance/project-membership.sql', import.meta.url), @@ -94,7 +95,18 @@ async function prepareAndEnforce(sql: Sql) { await applyMigration(sql, migration) } -async function runRegisteredProjectMigration(url: string) { +async function runRegisteredProjectMigration(url: string, reviews?: ProjectGroupingReview[]) { + const previousPath = process.env.PROJECT_BACKFILL_REVIEW_PATH + const previousUrl = process.env.MIGRATION_DATABASE_URL + const directory = reviews ? await mkdtemp(join(tmpdir(), 'project-review-')) : null + if (directory) { + process.env.PROJECT_BACKFILL_REVIEW_PATH = join(directory, 'manifest.json') + process.env.MIGRATION_DATABASE_URL = url + await writeFile( + process.env.PROJECT_BACKFILL_REVIEW_PATH, + JSON.stringify({ version: 1, databaseId: projectBackfillDatabaseId(url), groupings: reviews }) + ) + } const runner = postgres(url, { max: 1, onnotice: () => undefined, max_lifetime: null }) try { await runScriptMigrations( @@ -103,6 +115,9 @@ async function runRegisteredProjectMigration(url: string) { ) } finally { await runner.end({ timeout: 2 }) + vi.stubEnv('PROJECT_BACKFILL_REVIEW_PATH', previousPath) + vi.stubEnv('MIGRATION_DATABASE_URL', previousUrl) + if (directory) await rm(directory, { recursive: true, force: true }) } } @@ -116,6 +131,151 @@ async function seed(sql: Sql) { const constraintFailure = (error: unknown) => getPostgresErrorCode(error) === '23514' describe('Project expand/backfill/contract against PostgreSQL', () => { + it('repairs fully assigned stale Project archive and scope without restoring environments or workflows', async () => { + await database(async (sql, url) => { + await sql`INSERT INTO project (id,name,owner_id,archived_at) VALUES + ('stale','Keep identity','owner','2026-01-01'), ('archived','Keep archive','owner',NULL)` + await sql`INSERT INTO workspace (id,name,owner_id,organization_id,project_id,archived_at) VALUES + ('root','Root','owner','org','stale','2026-01-01'), + ('old','Old','owner',NULL,'archived','2026-01-02')` + await sql`INSERT INTO workspace (id,name,owner_id,organization_id,project_id,forked_from_workspace_id) + VALUES ('child','Child','owner','org','stale','root')` + await sql`INSERT INTO workflow VALUES ('old-flow','root','2026-01-01'), ('live-flow','child',NULL)` + const before = await sql`SELECT id,archived_at FROM workspace ORDER BY id` + const workflows = await sql`SELECT * FROM workflow ORDER BY id` + await runRegisteredProjectMigration(url) + expect( + await sql`SELECT id,name,organization_id,archived_at::text FROM project ORDER BY id` + ).toEqual([ + { + id: 'archived', + name: 'Keep archive', + organization_id: null, + archived_at: '2026-01-02 00:00:00', + }, + { id: 'stale', name: 'Keep identity', organization_id: 'org', archived_at: null }, + ]) + expect(await sql`SELECT id,archived_at FROM workspace ORDER BY id`).toEqual(before) + expect(await sql`SELECT * FROM workflow ORDER BY id`).toEqual(workflows) + expect(Object.values(await verifyProjectBackfill(sql)).every((count) => count === 0)).toBe( + true + ) + }) + }) + + it('reports fully assigned mixed-scope and personal-owner conflicts without changing ownership', async () => { + await database(async (sql) => { + await sql`INSERT INTO "user" VALUES ('other')` + await sql`INSERT INTO project (id,name,owner_id) VALUES ('shared','Shared','owner')` + await sql`INSERT INTO workspace (id,name,owner_id,project_id) VALUES ('root','Root','owner','shared')` + await sql`INSERT INTO workspace (id,name,owner_id,project_id,forked_from_workspace_id) + VALUES ('child','Child','other','shared','root')` + expect((await discoverProjectBackfill(sql, 'fixture')).conflicts).toEqual([ + { id: 'root', reason: expect.stringContaining('owners') }, + ]) + await sql`UPDATE workspace SET organization_id = 'org' WHERE id = 'child'` + expect((await discoverProjectBackfill(sql, 'fixture')).conflicts).toEqual([ + { id: 'root', reason: expect.stringContaining('organizations') }, + ]) + expect(await sql`SELECT owner_id FROM project`).toEqual([{ owner_id: 'owner' }]) + }) + }) + + it('reports unproven multi-root grouping and accepts an exact reviewed retention decision', async () => { + await database(async (sql, url) => { + await sql`INSERT INTO project (id,name,owner_id) VALUES ('shared','Keep context','owner')` + await sql`INSERT INTO workspace (id,name,owner_id,project_id) VALUES + ('root','Root','owner','shared'), ('detached','Detached','owner','shared')` + expect((await discoverProjectBackfill(sql, 'fixture')).conflicts).toContainEqual({ + id: 'shared', + reason: expect.stringContaining('grouping'), + }) + await expect(runRegisteredProjectMigration(url)).rejects.toThrow('conflicts') + expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'detached', project_id: 'shared' }, + { id: 'root', project_id: 'shared' }, + ]) + const reviews = (await discoverProjectBackfill(sql, 'fixture')).groupings.map((group) => ({ + ...group, + decision: 'retain' as const, + })) + await runRegisteredProjectMigration(url, reviews) + expect(await sql`SELECT id,name FROM project`).toEqual([ + { id: 'shared', name: 'Keep context' }, + ]) + }) + }) + + it.each(['root', 'unassigned-child'])( + 'rediscoveries Project-wide evidence after a %s changes during repair planning', + async (changed) => { + await database(async (sql) => { + await sql`INSERT INTO project (id,name,owner_id,archived_at) VALUES ('shared','Shared','owner','2026-01-01')` + await sql`INSERT INTO workspace (id,name,owner_id,project_id) VALUES ('a','A','owner','shared'),('b','B','owner','shared')` + await sql`INSERT INTO project_workspace (project_id,workspace_id) VALUES ('shared','a'),('shared','b')` + await sql`INSERT INTO workspace (id,name,owner_id,forked_from_workspace_id) VALUES ('c','Pending child','owner','b')` + const manifest = await discoverProjectBackfill(sql, 'fixture') + await sql`UPDATE workspace SET archived_at = '2026-01-02' WHERE id = ${changed === 'root' ? 'b' : 'c'}` + await expect(assignProjectBackfillBatch(sql, [manifest.families[0]])).rejects.toThrow( + 'rediscover' + ) + expect(await sql`SELECT archived_at IS NOT NULL AS archived FROM project`).toEqual([ + { archived: true }, + ]) + await prepareAndEnforce(sql) + expect(await sql`SELECT archived_at FROM project`).toEqual([{ archived_at: null }]) + }) + } + ) + + it.each([false, true])( + 'replays a reviewed partial family after commit while preserving legitimate shared roots (shared=%s)', + async (shared) => { + await database(async (sql) => { + await sql`INSERT INTO project(id,name,owner_id) VALUES ('existing','Keep context','owner')` + await sql`INSERT INTO workspace(id,name,owner_id) VALUES ('root','Root','owner')` + await sql`INSERT INTO workspace(id,name,owner_id,forked_from_workspace_id) VALUES ('child','Child','owner','root')` + await sql`INSERT INTO project_workspace(project_id,workspace_id) VALUES ('existing','root')` + if (shared) { + await sql`INSERT INTO workspace(id,name,owner_id,project_id) VALUES ('other-root','Other','owner','existing')` + await sql`INSERT INTO project_workspace(project_id,workspace_id) VALUES ('existing','other-root')` + await sql`INSERT INTO workspace(id,name,owner_id,forked_from_workspace_id) VALUES ('other-child','Other child','owner','other-root')` + } + const manifest = await discoverProjectBackfill(sql, 'fixture') + const family = manifest.families.find((item) => item.rootId === 'root') + if (!family) throw new Error('Missing partial family') + expect(await assignProjectBackfillBatch(sql, [family])).toMatchObject({ + assigned: 2, + projectsCreated: 0, + }) + expect(await assignProjectBackfillBatch(sql, [family])).toMatchObject({ + assigned: 0, + alreadyAssigned: 2, + projectsCreated: 0, + }) + if (shared) { + const other = manifest.families.find((item) => item.rootId === 'other-root') + if (!other) throw new Error('Missing other partial family') + expect(await assignProjectBackfillBatch(sql, [other])).toMatchObject({ + assigned: 1, + projectsCreated: 0, + }) + expect(await assignProjectBackfillBatch(sql, [other])).toMatchObject({ + assigned: 0, + alreadyAssigned: 2, + projectsCreated: 0, + }) + } + expect(await sql`SELECT id,name FROM project`).toEqual([ + { id: 'existing', name: 'Keep context' }, + ]) + expect( + await sql`SELECT id FROM workspace WHERE project_id IS DISTINCT FROM 'existing'` + ).toHaveLength(0) + }) + } + ) + it('runs registered assignment before enforcement for forks, singletons and archived families', async () => { await database(async (sql, url) => { await sql`INSERT INTO workspace (id,name,owner_id,archived_at) VALUES @@ -924,7 +1084,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { } try { await sql`INSERT INTO project (id,name,owner_id) VALUES ('empty','Empty','owner')` - await expect(run()).rejects.toThrow('validation failed') + await expect(run()).rejects.toThrow('conflicts') expect(await sql`SELECT * FROM drizzle.__drizzle_migrations`).toHaveLength(2) expect(await sql`SELECT * FROM script_migrations`).toHaveLength(0) await sql`DELETE FROM project WHERE id = 'empty'` @@ -1373,15 +1533,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) - it.each([ - 'cycle', - 'assigned-cycle', - 'split', - 'scope', - 'archive', - 'outside-family', - 'oversized', - ] as const)( + it.each(['cycle', 'assigned-cycle', 'split', 'oversized'] as const)( 'refuses %s legacy data without silently changing existing assignments', async (scenario) => { await database(async (sql) => { @@ -1400,13 +1552,6 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { if (scenario === 'split') { await sql`INSERT INTO project (id, name, owner_id) VALUES ('second', 'Second', 'owner')` await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('second', 'child')` - } else if (scenario === 'scope') { - await sql`UPDATE project SET organization_id = 'org'` - } else if (scenario === 'archive') { - await sql`UPDATE project SET archived_at = now()` - } else { - await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('outside', 'Outside', 'owner')` - await sql`INSERT INTO project_workspace (project_id, workspace_id) VALUES ('existing', 'outside')` } } const before = await sql`SELECT w.id, coalesce(w.project_id, pw.project_id) AS project_id @@ -1424,11 +1569,6 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sql`SELECT w.id, coalesce(w.project_id, pw.project_id) AS project_id FROM workspace w LEFT JOIN project_workspace pw ON pw.workspace_id = w.id ORDER BY w.id` ).toEqual(before) - if (scenario === 'outside-family') { - expect( - await sql`SELECT id FROM workspace WHERE id IN ('root','child') AND project_id IS NOT NULL` - ).toHaveLength(0) - } }) } ) @@ -1459,6 +1599,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) SELECT 'other-' || n, 'Other', 'owner', ${shape === 'large-family' ? 'root' : null} FROM generate_series(1, ${count}) n` await sql`UPDATE workspace SET project_id = 'existing'` + await sql`INSERT INTO project_workspace (project_id, workspace_id) SELECT 'existing', id FROM workspace` await prepareAndEnforce(sql) expect(await sql`SELECT id, name FROM project`).toEqual([ { id: 'existing', name: 'Keep this Project' }, diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index 9dc1612b850..939173115a3 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -4,7 +4,11 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { fileURLToPath } from 'node:url' import drizzleConfig from '@sim/db/drizzle.config' -import { ensureProjectArchiveRepairJournal } from '@sim/db/maintenance/project-repairs' +import { + completeProjectArchiveRepair, + countPendingProjectArchiveRepairs, + ensureProjectArchiveRepairJournal, +} from '@sim/db/maintenance/project-repairs' import { prepareForcedPush } from '@sim/db/scripts/prepare-push' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { generateId } from '@sim/utils/id' @@ -159,6 +163,25 @@ export const knowledgeBases = pgTable('knowledge_base', { ]) }, 30_000) + it('keeps the provider-cleanup gate visible across session search paths', async () => { + const scoped = postgres(fixtureUrl, { + max: 1, + connection: { search_path: 'old_scope,public' }, + onnotice: () => {}, + }) + try { + await sql`CREATE SCHEMA old_scope` + await ensureProjectArchiveRepairJournal(scoped) + const repair = { workspaceId: 'archived', archivedAt: '2026-01-01', workflowIds: ['flow'] } + await scoped`INSERT INTO project_backfill_archive_repairs (workspace_id,repair) VALUES ('archived',${JSON.stringify(repair)}::text::jsonb)` + expect(await countPendingProjectArchiveRepairs(sql)).toBe(1) + await completeProjectArchiveRepair(sql, repair) + expect(await countPendingProjectArchiveRepairs(scoped)).toBe(0) + } finally { + await scoped.end() + } + }) + it.each([false, true])( 'uses the direct migration connection for Project reconciliation (application URL present=%s)', async (applicationUrlPresent) => { @@ -216,7 +239,23 @@ export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), pr 30_000 ) - it('installs Project lifecycle enforcement for fresh schema push and preserves it on replay', async () => { + it('installs Project lifecycle enforcement through the fresh-push reconciler and preserves it on replay', async () => { + function reconcileProjects() { + const result = spawnSync( + 'bun', + [ + '--no-env-file', + fileURLToPath(new URL('./reconcile-project-membership.ts', import.meta.url)), + ], + { + env: { ...process.env, DATABASE_URL: fixtureUrl, MIGRATION_DATABASE_URL: fixtureUrl }, + encoding: 'utf8', + timeout: 15000, + } + ) + expect(result.error).toBeUndefined() + expect(result.status, result.stdout + result.stderr).toBe(0) + } await schema(`export const projects = pgTable('project', { id: text('id').primaryKey(), name: text('name').notNull(), ownerId: text('owner_id').notNull(), organizationId: text('organization_id'), archivedAt: timestamp('archived_at'), updatedAt: timestamp('updated_at').notNull().defaultNow(), @@ -229,8 +268,10 @@ export const workspaces = pgTable('workspace', { export const workflows = pgTable('workflow', { id: text('id').primaryKey(), workspaceId: text('workspace_id'), archivedAt: timestamp('archived_at'), })`) - const first = runPush(['--force']) + const first = push() expect(first.error, first.stderr).toBeUndefined() + expect(first.status, first.stdout + first.stderr).toBe(0) + reconcileProjects() await expect( sql`INSERT INTO project (id,name,owner_id) VALUES ('empty','Empty','owner')` ).rejects.toMatchObject({ code: '23514' }) @@ -238,8 +279,10 @@ export const workflows = pgTable('workflow', { await tx`INSERT INTO project (id,name,owner_id) VALUES ('family','Family','owner')` await tx`INSERT INTO workspace (id,name,owner_id,project_id,forked_from_workspace_id) VALUES ('root','Root','owner','family',NULL), ('fork','Fork','owner','family','root')` }) - const repeated = runPush(['--force']) + const repeated = push() expect(repeated.error, repeated.stderr).toBeUndefined() + expect(repeated.status, repeated.stdout + repeated.stderr).toBe(0) + reconcileProjects() expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual([ { id: 'fork', project_id: 'family' }, { id: 'root', project_id: 'family' }, diff --git a/packages/db/testing/workspace-fixtures.ts b/packages/db/testing/workspace-fixtures.ts index 6c2fc47853f..95480915b00 100644 --- a/packages/db/testing/workspace-fixtures.ts +++ b/packages/db/testing/workspace-fixtures.ts @@ -4,6 +4,8 @@ import { generateId } from '@sim/utils/id' import { and, eq, inArray, notExists, type SQL, sql } from 'drizzle-orm' import type { PgInsertValue } from 'drizzle-orm/pg-core' +const fixtureProjects = new WeakMap>() + type FixtureDatabase = typeof db type FixtureTransaction = Parameters[0]>[0] type WorkspaceRow = Omit< @@ -20,7 +22,8 @@ export async function insertWorkspaceFixture( database: FixtureDatabase | FixtureTransaction, values: WorkspaceRow | WorkspaceRow[] ) { - return database.transaction(async (tx) => { + const createdProjectIds = new Set() + const rows = await database.transaction(async (tx) => { const inputs = Array.isArray(values) ? values : [values] const byId = new Map(inputs.map((row) => [row.id, row])) const children = new Map() @@ -35,7 +38,6 @@ export async function insertWorkspaceFixture( for (const row of ordered) ordered.push(...(children.get(row.id) ?? [])) if (ordered.length !== inputs.length) throw new Error('Workspace fixture contains a fork cycle') const inserted = new Map() - const createdProjectIds = new Set() for (const row of ordered) { const [parent] = row.forkedFromWorkspaceId ? await tx @@ -82,6 +84,10 @@ export async function insertWorkspaceFixture( return created }) }) + const owned = fixtureProjects.get(database) ?? new Set() + for (const id of createdProjectIds) owned.add(id) + fixtureProjects.set(database, owned) + return rows } /** Removes fixture environments and their now-empty Projects in the same transaction. */ @@ -96,17 +102,18 @@ export async function deleteWorkspaceFixture( .from(workspace) .where(condition) await tx.delete(workspace).where(condition) - if (rows.length) - await tx.delete(project).where( - and( - inArray( - project.id, - rows.map((row) => row.projectId) - ), - notExists( - tx.select({ one: sql`1` }).from(workspace).where(eq(workspace.projectId, project.id)) + const owned = fixtureProjects.get(database) + const projectIds = rows.map((row) => row.projectId).filter((id) => owned?.has(id)) + if (projectIds.length) + await tx + .delete(project) + .where( + and( + inArray(project.id, projectIds), + notExists( + tx.select({ one: sql`1` }).from(workspace).where(eq(workspace.projectId, project.id)) + ) ) ) - ) }) } From d9743bb0237e683dee29a37efed40d26e8ece1c6 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 17:26:09 -0700 Subject: [PATCH 079/113] fix(projects): preserve backfill repair and resume guarantees Lock and verify effective legacy memberships before materializing reviewed assignments and applying the shared detach policy. Reject resume reports whose code hash differs before changing their checkpoint. Isolate local pubsub subscriber failures so healthy archive subscribers still receive notifications. Extend existing CLI and real-database integration coverage for mixed memberships, concurrent and stale connector changes, unchanged mismatched reports, replay, and local subscriber delivery. --- apps/sim/lib/events/pubsub.ts | 11 +- .../backfill-repair.integration.ts | 136 +++++++++++------- apps/sim/lib/projects/backfill-repair.ts | 32 ++++- apps/sim/scripts/backfill-projects.ts | 5 +- .../scripts/project-contract.integration.ts | 10 ++ 5 files changed, 135 insertions(+), 59 deletions(-) diff --git a/apps/sim/lib/events/pubsub.ts b/apps/sim/lib/events/pubsub.ts index 9550ae4db8d..345a082106a 100644 --- a/apps/sim/lib/events/pubsub.ts +++ b/apps/sim/lib/events/pubsub.ts @@ -162,9 +162,16 @@ class LocalPubSubChannel implements PubSubChannel { } subscribe(handler: (event: T) => void): () => void { - this.emitter.on(this.config.channel, handler) + const isolatedHandler = (event: T) => { + try { + handler(event) + } catch (error) { + logger.error(`Error in ${this.config.label} handler:`, error) + } + } + this.emitter.on(this.config.channel, isolatedHandler) return () => { - this.emitter.off(this.config.channel, handler) + this.emitter.off(this.config.channel, isolatedHandler) } } diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts index 8e3772e4ed7..97c1fcd09e4 100644 --- a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -182,61 +182,91 @@ describe('Operator archive repair against the full compatible schema', () => { 60000 ) - it('repairs an explicitly reviewed detached family without changing environments and replays a lost report', async () => { - const artifacts = { - manifest: join(directory, 'detach-manifest.json'), - report: join(directory, 'detach-report.json'), - } - try { - await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) + it.each(['column', 'mixed'] as const)( + 'repairs an explicitly reviewed detached family with %s membership and replays a lost report', + async (membership) => { + const artifacts = { + manifest: join(directory, `detach-${membership}-manifest.json`), + report: join(directory, `detach-${membership}-report.json`), + } + try { + await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) VALUES ('detach-owner','Owner','detach@fixture.test',true,now(),now())` - await client`INSERT INTO project (id,name,owner_id) VALUES ('detach-project','Keep context','detach-owner')` - await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id) + await client`INSERT INTO project (id,name,owner_id) VALUES ('detach-project','Keep context','detach-owner')` + await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id) VALUES ('retained-root','Retained','detach-owner','detach-owner','detach-project'), ('detached-root','Detached','detach-owner','detach-owner','detach-project')` - await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id,forked_from_workspace_id) + await client`INSERT INTO workspace (id,name,owner_id,billed_account_user_id,project_id,forked_from_workspace_id) VALUES ('detached-child','Child','detach-owner','detach-owner','detach-project','detached-root')` - await expect(run('plan', artifacts)).rejects.toMatchObject({ code: 2 }) - const plan = JSON.parse(await readFile(artifacts.manifest, 'utf8')) - plan.groupings[0].decision = 'detach' - plan.groupings[0].detachRootId = 'detached-root' - await writeFile(artifacts.manifest, JSON.stringify(plan)) - const before = - await client`SELECT id,owner_id,organization_id,archived_at,forked_from_workspace_id FROM workspace ORDER BY id` - const holder = postgres(url.toString(), { max: 1 }) - try { - await holder.begin(async (tx) => { - await tx`SELECT pg_advisory_xact_lock(hashtextextended('project:detach-project',0))` - await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) - expect(await client`SELECT id FROM project WHERE owner_id='detach-owner'`).toHaveLength(1) - }) + if (membership === 'mixed') { + await client`INSERT INTO project_workspace (project_id,workspace_id) + VALUES ('detach-project','retained-root'),('detach-project','detached-child')` + await client`UPDATE workspace SET project_id=NULL WHERE id IN ('retained-root','detached-child')` + } + await expect(run('plan', artifacts)).rejects.toMatchObject({ code: 2 }) + const plan = JSON.parse(await readFile(artifacts.manifest, 'utf8')) + plan.groupings[0].decision = 'detach' + plan.groupings[0].detachRootId = 'detached-root' + await writeFile(artifacts.manifest, JSON.stringify(plan)) + const before = + await client`SELECT id,owner_id,organization_id,archived_at,forked_from_workspace_id FROM workspace ORDER BY id` + const holder = postgres(url.toString(), { max: 1 }) + try { + await holder.begin(async (tx) => { + await tx`SELECT pg_advisory_xact_lock(hashtextextended('project:detach-project',0))` + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + expect(await client`SELECT id FROM project WHERE owner_id='detach-owner'`).toHaveLength( + 1 + ) + }) + if (membership === 'mixed') { + await client`INSERT INTO project (id,name,owner_id) VALUES ('changed-project','Changed','detach-owner')` + await holder.begin(async (tx) => { + await tx`UPDATE project_workspace SET project_id='changed-project' WHERE workspace_id='retained-root'` + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + expect( + await client`SELECT project_id FROM workspace WHERE id='retained-root'` + ).toEqual([{ project_id: null }]) + expect( + await client`SELECT id FROM project WHERE id=${plan.groupings[0].destinationProjectId}` + ).toHaveLength(0) + }) + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 1 }) + expect(await client`SELECT project_id FROM workspace WHERE id='retained-root'`).toEqual( + [{ project_id: null }] + ) + await client`UPDATE project_workspace SET project_id='detach-project' WHERE workspace_id='retained-root'` + await client`DELETE FROM project WHERE id='changed-project'` + } + } finally { + await holder.end() + } + await client`UPDATE workspace SET archived_at='2026-05-01' WHERE id='detached-child'` + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 1 }) + expect(await client`SELECT id FROM project WHERE owner_id='detach-owner'`).toHaveLength(1) + await client`UPDATE workspace SET archived_at=NULL WHERE id='detached-child'` + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + const destination = plan.groupings[0].destinationProjectId + expect( + await client`SELECT id FROM workspace WHERE project_id = ${destination} ORDER BY id` + ).toEqual([{ id: 'detached-child' }, { id: 'detached-root' }]) + expect(await client`SELECT id,name FROM project WHERE id = 'detach-project'`).toEqual([ + { id: 'detach-project', name: 'Keep context' }, + ]) + expect( + await client`SELECT id,owner_id,organization_id,archived_at,forked_from_workspace_id FROM workspace ORDER BY id` + ).toEqual(before) + await rm(artifacts.report) + await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) + expect(await client`SELECT id FROM project WHERE owner_id = 'detach-owner'`).toHaveLength(2) } finally { - await holder.end() + await client`DELETE FROM workspace WHERE owner_id = 'detach-owner'` + await client`DELETE FROM project WHERE owner_id = 'detach-owner'` + await client`DELETE FROM "user" WHERE id = 'detach-owner'` } - await client`UPDATE workspace SET archived_at='2026-05-01' WHERE id='detached-child'` - await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 1 }) - expect(await client`SELECT id FROM project WHERE owner_id='detach-owner'`).toHaveLength(1) - await client`UPDATE workspace SET archived_at=NULL WHERE id='detached-child'` - await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) - const destination = plan.groupings[0].destinationProjectId - expect( - await client`SELECT id FROM workspace WHERE project_id = ${destination} ORDER BY id` - ).toEqual([{ id: 'detached-child' }, { id: 'detached-root' }]) - expect(await client`SELECT id,name FROM project WHERE id = 'detach-project'`).toEqual([ - { id: 'detach-project', name: 'Keep context' }, - ]) - expect( - await client`SELECT id,owner_id,organization_id,archived_at,forked_from_workspace_id FROM workspace ORDER BY id` - ).toEqual(before) - await rm(artifacts.report) - await expect(run('repair', artifacts)).rejects.toMatchObject({ code: 2 }) - expect(await client`SELECT id FROM project WHERE owner_id = 'detach-owner'`).toHaveLength(2) - } finally { - await client`DELETE FROM workspace WHERE owner_id = 'detach-owner'` - await client`DELETE FROM project WHERE owner_id = 'detach-owner'` - await client`DELETE FROM "user" WHERE id = 'detach-owner'` - } - }, 60000) + }, + 60000 + ) it('keeps a Project active when another active environment still uses legacy membership', async () => { const artifacts = { @@ -265,7 +295,7 @@ describe('Operator archive repair against the full compatible schema', () => { } }, 60000) - it('completes provider cleanup despite a throwing local MCP subscriber', async () => { + it('notifies healthy local MCP subscribers and completes cleanup after another subscriber throws', async () => { const serverId = generateId() try { await client`INSERT INTO "user" (id,name,email,email_verified,created_at,updated_at) @@ -288,13 +318,17 @@ describe('Operator archive repair against the full compatible schema', () => { import { repairArchivedProjectEnvironment } from '@/lib/projects/backfill-repair'; import { mcpPubSub } from '@/lib/mcp/pubsub'; import { db, dbReplica } from '@sim/db'; + import assert from 'node:assert/strict'; import postgres from 'postgres'; const client = postgres(process.env.MIGRATION_DATABASE_URL, {max:1}); const unsubscribe = mcpPubSub.onWorkflowToolsChanged(() => { throw new Error('fixture subscriber failed'); }); + const received = []; + const unsubscribeHealthy = mcpPubSub.onWorkflowToolsChanged((event) => received.push(event)); try { await repairArchivedProjectEnvironment(client, {workspaceId:'local-env', archivedAt:'2026-05-01 00:00:00', workflowIds:['local-flow']}, 'local-subscriber'); + assert.ok(received.some((event) => event.serverId === '${serverId}' && event.workspaceId === 'local-env'), 'Healthy subscriber missed the archive notification'); } finally { - unsubscribe(); mcpPubSub.dispose(); await client.end(); + unsubscribe(); unsubscribeHealthy(); mcpPubSub.dispose(); await client.end(); await Promise.all([...new Set([db.$client,dbReplica.$client])].map(client=>client.end())); } `, diff --git a/apps/sim/lib/projects/backfill-repair.ts b/apps/sim/lib/projects/backfill-repair.ts index 1e5e5a36ba4..1c93bf30506 100644 --- a/apps/sim/lib/projects/backfill-repair.ts +++ b/apps/sim/lib/projects/backfill-repair.ts @@ -153,11 +153,11 @@ export async function repairProjectGrouping( !review.roots.includes(rootId) || review.destinationProjectId === review.projectId || review.members.length > 1000 || - review.members.some((row) => row.projectId !== review.projectId) || + review.members.some((row) => (row.projectId ?? row.legacyProjectId) !== review.projectId) || projectGroupingEvidence(review.members) !== review.evidence ) throw new ProjectBackfillConflict( - 'Detach requires an exact reviewed, fully assigned Project; rediscover after apply' + 'Detach requires an exact reviewed Project grouping; rediscover' ) const expected = new Map(review.members.map((row) => [row.id, row])) const root = expected.get(rootId) @@ -185,14 +185,32 @@ export async function repairProjectGrouping( ] if (!(await tryAcquireAdvisoryXactLocks(tx, 'project_backfill', keys))) throw new ProjectBackfillBusy('Reviewed Project grouping is busy') + const reviewedIds = review.members.map((row) => row.id) + await tx.execute(sql`SELECT id FROM workspace + WHERE ${inArray(schema.workspace.id, reviewedIds)} + ORDER BY id COLLATE "C" FOR NO KEY UPDATE NOWAIT`) + const [legacy] = await tx.execute<{ present: boolean }>( + sql`SELECT to_regclass('public.project_workspace') IS NOT NULL AS present` + ) + if (legacy?.present) { + await tx.execute(sql`SELECT pw.workspace_id FROM project_workspace pw + JOIN workspace ON workspace.id = pw.workspace_id + WHERE ${inArray(schema.workspace.id, reviewedIds)} AND workspace.project_id IS NULL + ORDER BY pw.workspace_id COLLATE "C" FOR SHARE OF pw NOWAIT`) + } + const legacyAssignment = legacy?.present + ? sql`(SELECT pw.project_id FROM project_workspace pw WHERE pw.workspace_id = workspace.id)` + : sql`NULL::text` const current = await tx.execute(sql` SELECT id, forked_from_workspace_id AS "parentId", owner_id AS "ownerId", - organization_id AS "organizationId", archived_at::text AS "archivedAt", project_id AS "projectId", NULL::text AS "legacyProjectId" - FROM workspace WHERE project_id IN (${review.projectId},${review.destinationProjectId}) + organization_id AS "organizationId", archived_at::text AS "archivedAt", project_id AS "projectId", ${legacyAssignment} AS "legacyProjectId" + FROM workspace WHERE coalesce(project_id, ${legacyAssignment}) IN (${review.projectId},${review.destinationProjectId}) ORDER BY id COLLATE "C" LIMIT 1001 FOR NO KEY UPDATE NOWAIT`) if (current.length > 1000) throw new ProjectBackfillConflict('Project grouping exceeds repair limit') - const destination = current.filter((row) => row.projectId === review.destinationProjectId) + const destination = current.filter( + (row) => (row.projectId ?? row.legacyProjectId) === review.destinationProjectId + ) if (destination.length) { if ( projectGroupingEvidence( @@ -220,6 +238,10 @@ export async function repairProjectGrouping( throw new ProjectBackfillConflict( 'Reviewed Project ownership or scope requires reconciliation before detach' ) + await tx + .update(schema.workspace) + .set({ projectId: review.projectId }) + .where(and(inArray(schema.workspace.id, reviewedIds), isNull(schema.workspace.projectId))) await splitForkProject(tx, rootId, review.destinationProjectId) }) } diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index 02101e7c145..5b19f2c3055 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -323,13 +323,16 @@ The registered migration backfills remaining assignments, verifies, then enforce } if (progress.manifestHash !== manifestHash || progress.databaseId !== databaseId) throw new Error('Report does not match the reviewed manifest') + if (progress.codeHash !== codeHash) + throw new Error( + 'Report belongs to different backfill code; use the original checkout or a new report' + ) if ( progress.nextIndex > manifest.families.length || progress.deferred.some((index) => index >= manifest.families.length) ) throw new Error('Invalid report cursor') progress.runId = generateId() - progress.codeHash = codeHash progress.status = 'running' const started = performance.now() let attempts = 0 diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 5206b248579..5a94166fdd5 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -613,6 +613,16 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { projectsCreated: 50, status: 'paused', }) + const mismatched = JSON.stringify({ ...report, codeHash: 'another-checkout' }) + await writeFile(reportPath, mismatched) + await expect(run('apply', ['--ack-release-drained'])).rejects.toMatchObject({ + code: 1, + stderr: expect.stringContaining('Report belongs to different backfill code'), + }) + expect(await readFile(reportPath, 'utf8')).toBe(mismatched) + expect(await sql`SELECT id FROM project`).toHaveLength(50) + expect(await sql`SELECT id FROM workspace WHERE project_id IS NOT NULL`).toHaveLength(50) + await writeFile(reportPath, JSON.stringify(report)) for (const [status, exitCode] of [ ['running', 2], ['paused', 2], From 639c8ca8ec6a2cff78b4ffc2235ee44681017ab0 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 17:33:30 -0700 Subject: [PATCH 080/113] test(projects): align fixture lifecycle with enforced project ownership --- .../cleanup-soft-deletes.integration.ts | 28 ++++++++++++------ .../search-source-setup.integration.ts | 29 ++++++++++++------- .../__integration__/fork-sync.integration.ts | 15 ++++++---- apps/sim/scripts/test-mobile-e2e.ts | 13 +++++++-- 4 files changed, 58 insertions(+), 27 deletions(-) diff --git a/apps/sim/background/cleanup-soft-deletes.integration.ts b/apps/sim/background/cleanup-soft-deletes.integration.ts index 3c1d7b920ed..ef87c72760b 100644 --- a/apps/sim/background/cleanup-soft-deletes.integration.ts +++ b/apps/sim/background/cleanup-soft-deletes.integration.ts @@ -28,18 +28,28 @@ async function seedTest(deletedAt: string | null, sizeBytes: number) { describe('Retention cleanup of deleted workflow tests', () => { beforeAll(async () => { - await control`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at) - VALUES (${userId}, 'Test cleanup fixture', ${`${userId}@example.test`}, true, now(), now())` - await control`INSERT INTO user_stats (id, user_id, storage_used_bytes) - VALUES (${generateId()}, ${userId}, 100)` - await control`INSERT INTO workspace (id, name, owner_id, billed_account_user_id, storage_used_bytes) - VALUES (${workspaceId}, 'Test cleanup fixtures', ${userId}, ${userId}, 100)` + await control.begin(async (tx) => { + await tx`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at) + VALUES (${userId}, 'Test cleanup fixture', ${`${userId}@example.test`}, true, now(), now())` + await tx`INSERT INTO user_stats (id, user_id, storage_used_bytes) + VALUES (${generateId()}, ${userId}, 100)` + await tx`INSERT INTO project (id, name, owner_id) + VALUES (${workspaceId}, 'Test cleanup project', ${userId})` + await tx`INSERT INTO workspace (id, project_id, name, owner_id, billed_account_user_id, storage_used_bytes) + VALUES (${workspaceId}, ${workspaceId}, 'Test cleanup fixtures', ${userId}, ${userId}, 100)` + }) }) afterAll(async () => { - await control`DELETE FROM workspace WHERE id = ${workspaceId}` - await control`DELETE FROM "user" WHERE id = ${userId}` - await control.end() + try { + await control.begin(async (tx) => { + await tx`DELETE FROM workspace WHERE id = ${workspaceId}` + await tx`DELETE FROM project WHERE id = ${workspaceId} AND owner_id = ${userId}` + await tx`DELETE FROM "user" WHERE id = ${userId}` + }) + } finally { + await control.end() + } }) it('purges an expired test with its source and releases the source bytes', async () => { diff --git a/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts b/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts index 6abf3136f94..45fd0ad3615 100644 --- a/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts +++ b/apps/sim/lib/knowledge/__integration__/search-source-setup.integration.ts @@ -11,9 +11,8 @@ import { user, workspace, } from '@sim/db/schema' -import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' -import { and, eq, isNull } from 'drizzle-orm' +import { and, eq, inArray, isNull } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' const fixture = vi.hoisted(() => ({ dispatch: vi.fn() })) @@ -95,12 +94,12 @@ describe('Search source identity and concurrent creation', () => { }) afterAll(async () => { - await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId)) - await deleteWorkspaceFixture(db, eq(workspace.id, other.workspaceId)) - for (const id of [ids.aliceId, ids.bobId, other.aliceId, other.bobId]) { - await db.delete(project).where(eq(project.ownerId, id)) - await db.delete(user).where(eq(user.id, id)) - } + const userIds = [ids.aliceId, ids.bobId, other.aliceId, other.bobId] + await db.transaction(async (tx) => { + await tx.delete(workspace).where(inArray(workspace.ownerId, userIds)) + await tx.delete(project).where(inArray(project.ownerId, userIds)) + await tx.delete(user).where(inArray(user.id, userIds)) + }) }) async function createSource(folderId: string) { @@ -179,8 +178,18 @@ describe('Search source identity and concurrent creation', () => { .where(eq(resourcePolicy.workspaceId, created.id)) expect(policies).toHaveLength(0) } finally { - await db.delete(permissions).where(eq(permissions.entityId, created.id)) - await deleteWorkspaceFixture(db, eq(workspace.id, created.id)) + await db.transaction(async (tx) => { + await tx.delete(permissions).where(eq(permissions.entityId, created.id)) + const [removed] = await tx + .delete(workspace) + .where(eq(workspace.id, created.id)) + .returning({ projectId: workspace.projectId }) + if (removed) { + await tx + .delete(project) + .where(and(eq(project.id, removed.projectId), eq(project.ownerId, ids.aliceId))) + } + }) } }) diff --git a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts index cfc27bfbe50..c2b3f30669a 100644 --- a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts +++ b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts @@ -9,6 +9,7 @@ import { knowledgeBase, outboxEvent, permissions, + project, user, userTableDefinitions, workflow, @@ -21,7 +22,7 @@ import { workspaceOperationReceipt, workspaceSandbox, } from '@sim/db/schema' -import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' +import { insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' import { generateId } from '@sim/utils/id' import { and, eq, inArray, sql } from 'drizzle-orm' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' @@ -184,11 +185,13 @@ describe('authorized fork and sync against PostgreSQL', () => { }) }) afterAll(async () => { - await deleteWorkspaceFixture( - db, - inArray(workspace.id, [sourceWorkspaceId, ...createdWorkspaceIds]) - ) - await db.delete(user).where(eq(user.id, userId)) + await db.transaction(async (tx) => { + await tx + .delete(workspace) + .where(inArray(workspace.id, [sourceWorkspaceId, ...createdWorkspaceIds])) + await tx.delete(project).where(eq(project.ownerId, userId)) + await tx.delete(user).where(eq(user.id, userId)) + }) await db.$client.end() }) diff --git a/apps/sim/scripts/test-mobile-e2e.ts b/apps/sim/scripts/test-mobile-e2e.ts index 0277dbb05b1..c546305682f 100644 --- a/apps/sim/scripts/test-mobile-e2e.ts +++ b/apps/sim/scripts/test-mobile-e2e.ts @@ -204,8 +204,10 @@ async function seed(): Promise { await tx`insert into "user" (id, name, email, normalized_email, email_verified, created_at, updated_at) values (${ownerId}, 'Mobile E2E', ${email}, ${email}, true, now(), now())` await tx`insert into user_stats (id, user_id) values (${generateId()}, ${ownerId})` - await tx`insert into workspace (id, name, owner_id, billed_account_user_id) - values (${workspaceId}, 'Mobile E2E workspace', ${ownerId}, ${ownerId})` + await tx`insert into project (id, name, owner_id) + values (${workspaceId}, 'Mobile E2E project', ${ownerId})` + await tx`insert into workspace (id, project_id, name, owner_id, billed_account_user_id) + values (${workspaceId}, ${workspaceId}, 'Mobile E2E workspace', ${ownerId}, ${ownerId})` await tx`insert into permissions (id, user_id, entity_type, entity_id, permission_type) values (${generateId()}, ${ownerId}, 'workspace', ${workspaceId}, 'admin')` await tx`insert into session (id, token, user_id, expires_at, created_at, updated_at) @@ -1071,9 +1073,16 @@ try { await sql.begin(async (tx) => { await tx`delete from permissions where entity_type = 'workspace' and entity_id = ${workspaceId}` await tx`delete from workspace where id = ${workspaceId}` + await tx`delete from project where id = ${workspaceId} and owner_id = ${ownerId}` await tx`delete from workflow_execution_snapshots where id = ${snapshotId}` await tx`delete from "user" where id = ${ownerId}` }) + const [remaining] = await sql`select + exists(select 1 from "user" where id = ${ownerId}) as owner, + exists(select 1 from workspace where id = ${workspaceId}) as workspace, + exists(select 1 from project where id = ${workspaceId}) as project, + exists(select 1 from workflow_execution_snapshots where id = ${snapshotId}) as snapshot` + assert.deepEqual(remaining, { owner: false, workspace: false, project: false, snapshot: false }) }) await sql.end() await check('same-origin HTTP responses succeed', undefined, async () => { From c70429367e5c0bd142300ff85c8be0cd843b446d Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 18:16:45 -0700 Subject: [PATCH 081/113] test(projects): skip archive repair suite when Redis is unset --- .../backfill-repair.integration.ts | 127 +++++++++--------- 1 file changed, 66 insertions(+), 61 deletions(-) diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts index 97c1fcd09e4..4be4fffc3aa 100644 --- a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -17,17 +17,16 @@ const name = `project_repair_test_${generateId().replaceAll('-', '')}` const url = new URL(readTestDatabaseUrl()) url.pathname = `/${name}` const client = postgres(url.toString(), { max: 1, onnotice: () => {} }) -const directory = await mkdtemp(join(tmpdir(), 'project-repair-test-')) -const manifest = join(directory, 'manifest.json') -const report = join(directory, 'report.json') +let directory: string +let manifest: string +let report: string const redisUrl = readTestRedisUrl() -if (!redisUrl) throw new Error('Archive repair integration requires TEST_REDIS_URL') -const cache = new Redis(redisUrl) -const subscriber = new Redis(redisUrl) +const describeWithRedis = describe.runIf(Boolean(redisUrl)) +let cache: Redis +let subscriber: Redis const mcpServerId = generateId() const cacheKey = `mcp:tools:workspace:env:server:${mcpServerId}` const published: unknown[] = [] -subscriber.on('message', (_channel, message) => published.push(JSON.parse(message))) const releaseNotifications = createDeferred() let tailNotified = false const realtime = createServer(async (request, response) => { @@ -64,63 +63,69 @@ const run = (command: string, artifacts = { manifest, report }) => } ) -beforeAll(async () => { - await new Promise((resolve) => realtime.listen(0, '127.0.0.1', resolve)) - const address = realtime.address() - if (!address || typeof address === 'string') throw new Error('Realtime fixture failed to bind') - environment.SOCKET_SERVER_URL = `http://127.0.0.1:${address.port}` - await subscriber.subscribe('mcp:workflow_tools_changed') - await admin.unsafe(`CREATE DATABASE "${name}"`) - await client.unsafe( - 'CREATE EXTENSION vector; CREATE EXTENSION btree_gin; CREATE EXTENSION pg_trgm' - ) - const folder = join(directory, 'migrations') - await mkdir(join(folder, 'meta'), { recursive: true }) - const expansion = journal.entries.find((entry) => entry.tag === '0404_workspace_project_column') - if (!expansion) throw new Error('Missing expansion migration') - const entries = journal.entries.filter((entry) => entry.when <= expansion.when) - const source = new URL('../../../../../packages/db/migrations/', import.meta.url) - for (const entry of entries) - await copyFile(new URL(`${entry.tag}.sql`, source), join(folder, `${entry.tag}.sql`)) - await writeFile(join(folder, 'meta/_journal.json'), JSON.stringify({ ...journal, entries })) - await execute( - 'bun', - [ - '--no-env-file', - '-e', - ` - import postgres from 'postgres'; - import { drizzle } from 'drizzle-orm/postgres-js'; - import { migrate } from 'drizzle-orm/postgres-js/migrator'; - import { runScriptMigrations, scriptMigrations } from '@sim/db/script-migrations/index'; - const sql = postgres(process.env.MIGRATION_DATABASE_URL,{max:1,onnotice:()=>{}}); - try { await migrate(drizzle(sql),{migrationsFolder:process.env.FIXTURE_MIGRATIONS}); await runScriptMigrations(sql, scriptMigrations.filter((item) => item.name !== '0031_project_membership')); } - finally { await sql.end(); } - `, - ], - { - cwd: new URL('../../../../../packages/db/', import.meta.url), - env: { ...environment, FIXTURE_MIGRATIONS: folder }, - timeout: 120000, - } - ) -}, 120000) +describeWithRedis('Operator archive repair against the full compatible schema', () => { + beforeAll(async () => { + directory = await mkdtemp(join(tmpdir(), 'project-repair-test-')) + manifest = join(directory, 'manifest.json') + report = join(directory, 'report.json') + cache = new Redis(redisUrl!) + subscriber = new Redis(redisUrl!) + subscriber.on('message', (_channel, message) => published.push(JSON.parse(message))) + await new Promise((resolve) => realtime.listen(0, '127.0.0.1', resolve)) + const address = realtime.address() + if (!address || typeof address === 'string') throw new Error('Realtime fixture failed to bind') + environment.SOCKET_SERVER_URL = `http://127.0.0.1:${address.port}` + await subscriber.subscribe('mcp:workflow_tools_changed') + await admin.unsafe(`CREATE DATABASE "${name}"`) + await client.unsafe( + 'CREATE EXTENSION vector; CREATE EXTENSION btree_gin; CREATE EXTENSION pg_trgm' + ) + const folder = join(directory, 'migrations') + await mkdir(join(folder, 'meta'), { recursive: true }) + const expansion = journal.entries.find((entry) => entry.tag === '0404_workspace_project_column') + if (!expansion) throw new Error('Missing expansion migration') + const entries = journal.entries.filter((entry) => entry.when <= expansion.when) + const source = new URL('../../../../../packages/db/migrations/', import.meta.url) + for (const entry of entries) + await copyFile(new URL(`${entry.tag}.sql`, source), join(folder, `${entry.tag}.sql`)) + await writeFile(join(folder, 'meta/_journal.json'), JSON.stringify({ ...journal, entries })) + await execute( + 'bun', + [ + '--no-env-file', + '-e', + ` + import postgres from 'postgres'; + import { drizzle } from 'drizzle-orm/postgres-js'; + import { migrate } from 'drizzle-orm/postgres-js/migrator'; + import { runScriptMigrations, scriptMigrations } from '@sim/db/script-migrations/index'; + const sql = postgres(process.env.MIGRATION_DATABASE_URL,{max:1,onnotice:()=>{}}); + try { await migrate(drizzle(sql),{migrationsFolder:process.env.FIXTURE_MIGRATIONS}); await runScriptMigrations(sql, scriptMigrations.filter((item) => item.name !== '0031_project_membership')); } + finally { await sql.end(); } + `, + ], + { + cwd: new URL('../../../../../packages/db/', import.meta.url), + env: { ...environment, FIXTURE_MIGRATIONS: folder }, + timeout: 120000, + } + ) + }, 120000) -afterAll(async () => { - releaseNotifications.resolve() - await new Promise((resolve, reject) => { - realtime.close((error) => (error ? reject(error) : resolve())) - realtime.closeAllConnections() + afterAll(async () => { + releaseNotifications.resolve() + await new Promise((resolve, reject) => { + realtime.close((error) => (error ? reject(error) : resolve())) + realtime.closeAllConnections() + }) + await cache.del(cacheKey) + await Promise.all([cache.quit(), subscriber.quit()]) + await client.end({ timeout: 2 }) + await admin.unsafe(`DROP DATABASE IF EXISTS "${name}" WITH (FORCE)`) + await admin.end() + await rm(directory, { recursive: true, force: true }) }) - await cache.del(cacheKey) - await Promise.all([cache.quit(), subscriber.quit()]) - await client.end({ timeout: 2 }) - await admin.unsafe(`DROP DATABASE IF EXISTS "${name}" WITH (FORCE)`) - await admin.end() - await rm(directory, { recursive: true, force: true }) -}) -describe('Operator archive repair against the full compatible schema', () => { it.each(['workspace', 'project', 'row'] as const)( 'defers a busy %s lock, repairs unrelated environments and resumes without losing progress', async (lock) => { From d0a9792707123adfdeed445fd433917128651f84 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 18:30:23 -0700 Subject: [PATCH 082/113] chore(projects): normalize enforcement SQL whitespace --- packages/db/maintenance/project-membership.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/db/maintenance/project-membership.sql b/packages/db/maintenance/project-membership.sql index f615b1a13d1..d15e41804c8 100644 --- a/packages/db/maintenance/project-membership.sql +++ b/packages/db/maintenance/project-membership.sql @@ -265,4 +265,3 @@ DROP FUNCTION pg_temp.validate_project_membership(); SET statement_timeout = 0; --> statement-breakpoint SET lock_timeout = '5s'; - From d8a9627a593c833f6f313e1e8af4c53768eaaa53 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 22:26:47 -0700 Subject: [PATCH 083/113] feat(projects): commit membership authority before reconciliation --- .github/scripts/check-project-rollout.py | 6 +- .github/workflows/migrate.yml | 7 +- .../backfill-repair.integration.ts | 12 ++ apps/sim/lib/projects/backfill-repair.ts | 3 + apps/sim/scripts/backfill-projects.ts | 10 +- packages/db/maintenance/project-backfill.ts | 2 + .../db/maintenance/project-membership.sql | 6 + packages/db/maintenance/project-rollout.ts | 89 ++++++++++ .../0406_project_membership_enforcement.sql | 2 +- .../0031_project_membership.ts | 3 + .../scripts/project-authority.integration.ts | 167 ++++++++++++++++++ .../db/scripts/project-contract-required.ts | 11 +- .../scripts/project-contract.integration.ts | 16 ++ packages/db/scripts/push.integration.ts | 14 +- .../scripts/reconcile-project-membership.ts | 2 + 15 files changed, 339 insertions(+), 11 deletions(-) create mode 100644 packages/db/maintenance/project-rollout.ts create mode 100644 packages/db/scripts/project-authority.integration.ts diff --git a/.github/scripts/check-project-rollout.py b/.github/scripts/check-project-rollout.py index 6cb93f55f05..a2f04734326 100644 --- a/.github/scripts/check-project-rollout.py +++ b/.github/scripts/check-project-rollout.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 """Read-only, fail-closed ECS retirement check for the Project column contract migration. -The expected digest acknowledges fully deployed #8830 column-only creation/reassignment -and retirement of pre-8830 servers and relevant old worker jobs. Every live and supported +The expected digest acknowledges fully deployed #8830 transaction-barrier and durable-authority support +and retirement of every incompatible server and relevant old worker job before switching. Every live and supported rollback application must tolerate project_workspace being absent. AWS checks below independently verify ECS retirement, not worker drainage. """ @@ -43,7 +43,7 @@ def matches_release(execution, digest): def verify(environment, region, digest): if not re.fullmatch(r'sha256:[0-9a-f]{64}', digest): - raise RuntimeError('Set the environment-specific PROJECT_COLUMN_ENFORCEMENT_READY_IMAGE_DIGEST after verifying #8830 connector-independent readers, column-only writers and pre-8830 server/worker drainage') + raise RuntimeError('Set the environment-specific PROJECT_COLUMN_ENFORCEMENT_READY_IMAGE_DIGEST after verifying #8830 authority-aware readers/writers, transaction barriers and all incompatible server/worker drainage') pipeline = f'sim-{environment}-{region}-app-deployment' execution = latest_execution(region, pipeline) if not matches_release(execution, digest): diff --git a/.github/workflows/migrate.yml b/.github/workflows/migrate.yml index f75cf2fc89b..2a91393d41d 100644 --- a/.github/workflows/migrate.yml +++ b/.github/workflows/migrate.yml @@ -76,10 +76,11 @@ jobs: role-to-assume: ${{ inputs.environment == 'production' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }} aws-region: ${{ inputs.environment == 'production' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }} - # Acknowledge #8830 only after connector-independent readers and column-only writers deploy and - # pre-8830 servers and relevant old Trigger.dev runs have drained. The registered Project migration backfills, validates, and removes the connector. + # Acknowledge #8830 only after authority-aware readers/writers deploy and all incompatible + # servers and relevant old Trigger.dev runs drain. The runner commits the column-authority + # switch before backfill, validation, and connector retirement; retries never revert it. # The preflight independently checks ECS retirement; it does not inspect worker runs. - - name: Require completed column-only rollout before Project enforcement + - name: Require completed authority-aware rollout before Project cutover if: steps.project-contract.outputs.required == 'true' env: ENVIRONMENT: ${{ inputs.environment }} diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts index 4d380b2bede..a0b04f9e015 100644 --- a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -4,6 +4,7 @@ import { createServer } from 'node:http' import { tmpdir } from 'node:os' import { join } from 'node:path' import { promisify } from 'node:util' +import { switchProjectMembershipAuthority } from '@sim/db/maintenance/project-rollout' import journal from '@sim/db/migrations/meta/_journal.json' import { readTestDatabaseUrl, readTestRedisUrl } from '@sim/db/testing/test-infrastructure' import { createDeferred } from '@sim/testing/helpers/deferred' @@ -111,6 +112,17 @@ describeWithRedis('Operator archive repair against the full compatible schema', timeout: 120000, } ) + await client.unsafe(`CREATE TABLE IF NOT EXISTS project_membership_rollout ( + id text PRIMARY KEY CHECK (id = 'membership'), + phase text NOT NULL DEFAULT 'connector' CONSTRAINT project_membership_rollout_phase CHECK (phase IN ('connector', 'column')) + )`) + await client`INSERT INTO project_membership_rollout (id, phase) VALUES ('membership', 'connector') ON CONFLICT DO NOTHING` + await client`SELECT pg_advisory_lock(hashtextextended('sim:project-backfill-operator', 0))` + try { + await switchProjectMembershipAuthority(client) + } finally { + await client`SELECT pg_advisory_unlock(hashtextextended('sim:project-backfill-operator', 0))` + } }, 120000) afterAll(async () => { diff --git a/apps/sim/lib/projects/backfill-repair.ts b/apps/sim/lib/projects/backfill-repair.ts index 1c93bf30506..cd4ea4ad3b8 100644 --- a/apps/sim/lib/projects/backfill-repair.ts +++ b/apps/sim/lib/projects/backfill-repair.ts @@ -10,6 +10,7 @@ import { completeProjectArchiveRepair, ensureProjectArchiveRepairJournal, } from '@sim/db/maintenance/project-repairs' +import { assertProjectColumnAuthority } from '@sim/db/maintenance/project-rollout' import * as schema from '@sim/db/schema' import { and, eq, inArray, isNull, sql } from 'drizzle-orm' import { drizzle } from 'drizzle-orm/postgres-js' @@ -31,6 +32,7 @@ export async function repairArchivedProjectEnvironment( repair: ProjectArchiveRepair, requestId: string ): Promise { + await assertProjectColumnAuthority(client) await ensureProjectArchiveRepairJournal(client) const connection = drizzle({ client, schema }) const effects = await connection.transaction(async (tx) => { @@ -146,6 +148,7 @@ export async function repairProjectGrouping( client: Sql, review: ProjectGroupingReview ): Promise { + await assertProjectColumnAuthority(client) const rootId = review.detachRootId if ( review.decision !== 'detach' || diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index 5b19f2c3055..2c03c4321e4 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -1,4 +1,5 @@ #!/usr/bin/env bun + import { createHash } from 'node:crypto' import { link, open, readFile, rename, stat, unlink } from 'node:fs/promises' import { resolve } from 'node:path' @@ -13,6 +14,7 @@ import { readProjectGroupingReviews, verifyProjectBackfill, } from '@sim/db/maintenance/project-backfill' +import { assertProjectColumnAuthority } from '@sim/db/maintenance/project-rollout' import { withUtcTimestamps } from '@sim/db/timestamps' import { createLogger } from '@sim/logger' import { describeError, getTransientDatabaseFailure } from '@sim/utils/errors' @@ -168,7 +170,7 @@ on an authorized operator host with that private manifest. Direct invocation doe AWS preflight: freshly verify the compatible deployed release, old app/worker drainage and release coordination first. Prior failed CI is not continuing proof if deployment state changed. Once the runner succeeds and writes its normal receipt, the ordinary deployment retry needs no local file. -The registered migration backfills remaining assignments, verifies, then enforces.\nExit: 0 complete, 2 running/paused/incomplete, 1 failure. Use db:migrate for routine backfill and enforcement after writer drainage.\n`) +The registered migration first commits the durable column-authority switch after draining incompatible writers, then backfills remaining assignments, verifies, and enforces. A failed migration never returns to connector mode. Run db:migrate once before repair/apply; its committed switch remains effective if discovery requires remediation.\nExit: 0 complete, 2 running/paused/incomplete, 1 failure. Use db:migrate for routine backfill and enforcement after writer drainage.\n`) return } const [command] = positionals @@ -242,6 +244,7 @@ The registered migration backfills remaining assignments, verifies, then enforce await sql`SELECT pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator',0)) AS held, pg_backend_pid() AS pid` if (!lock.held) throw new Error('Another Project preparation or enforcement runner is active') lockPid = lock.pid + if (live) await assertProjectColumnAuthority(sql) if (command === 'plan') { if (!manifestPath) throw new Error('plan requires --manifest') const reviews = values.review @@ -295,6 +298,11 @@ The registered migration backfills remaining assignments, verifies, then enforce new URL('../../../packages/db/maintenance/project-backfill.ts', import.meta.url) ) ) + .update( + await readFile( + new URL('../../../packages/db/maintenance/project-rollout.ts', import.meta.url) + ) + ) .update(await readFile(new URL('../lib/projects/backfill-repair.ts', import.meta.url))) .digest('hex') let progress: z.infer diff --git a/packages/db/maintenance/project-backfill.ts b/packages/db/maintenance/project-backfill.ts index beb7c3f591a..ecdecba7c9f 100644 --- a/packages/db/maintenance/project-backfill.ts +++ b/packages/db/maintenance/project-backfill.ts @@ -1,6 +1,7 @@ import { createHash } from 'node:crypto' import { readFile, stat } from 'node:fs/promises' import { countPendingProjectArchiveRepairs } from '@sim/db/maintenance/project-repairs' +import { assertProjectColumnAuthority } from '@sim/db/maintenance/project-rollout' import { createLogger } from '@sim/logger' import { generateId } from '@sim/utils/id' import { isRecordLike } from '@sim/utils/object' @@ -471,6 +472,7 @@ export async function assignProjectBackfillBatch(sql: Sql, families: ProjectBack await tx`SET LOCAL statement_timeout = '3s'` await tx`SET LOCAL lock_timeout = '250ms'` const legacy = await hasLegacyMemberships(tx) + await assertProjectColumnAuthority(tx) const roots = families.map((f) => f.rootId).sort(compareStrings) const expected = families.flatMap((f) => f.members).sort((a, b) => compareStrings(a.id, b.id)) const ids = expected.map((row) => row.id) diff --git a/packages/db/maintenance/project-membership.sql b/packages/db/maintenance/project-membership.sql index d15e41804c8..75e659ae3c8 100644 --- a/packages/db/maintenance/project-membership.sql +++ b/packages/db/maintenance/project-membership.sql @@ -4,6 +4,12 @@ COMMIT; SET statement_timeout = '60s'; --> statement-breakpoint DO $$ BEGIN + IF to_regclass('public.project_membership_rollout') IS NULL THEN + RAISE EXCEPTION 'Project membership rollout marker is missing' USING ERRCODE = '55000'; + END IF; + IF NOT EXISTS (SELECT 1 FROM public.project_membership_rollout WHERE id = 'membership' AND phase = 'column') THEN + RAISE EXCEPTION 'Project membership authority must switch before enforcement' USING ERRCODE = '55000'; + END IF; IF NOT pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator', 0)) THEN RAISE EXCEPTION 'Project preparation is still running; finish it before enforcement' USING ERRCODE = '55000'; END IF; diff --git a/packages/db/maintenance/project-rollout.ts b/packages/db/maintenance/project-rollout.ts new file mode 100644 index 00000000000..5c329447858 --- /dev/null +++ b/packages/db/maintenance/project-rollout.ts @@ -0,0 +1,89 @@ +import { getTransientDatabaseFailure } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { backoffWithJitter } from '@sim/utils/retry' +import type { Sql, TransactionSql } from 'postgres' + +type ProjectMembershipPhase = 'connector' | 'column' + +/** Reads durable authority without inferring it from partially copied membership. */ +export async function readProjectMembershipPhase( + sql: Sql | TransactionSql +): Promise { + const [table] = + await sql`SELECT to_regclass('public.project_membership_rollout') IS NOT NULL AS present` + if (!table?.present) throw new Error('Project membership rollout marker is missing') + const rows = await sql< + { id: string; phase: string }[] + >`SELECT id, phase FROM public.project_membership_rollout` + const row = rows[0] + if (rows.length !== 1 || row?.id !== 'membership' || !['connector', 'column'].includes(row.phase)) + throw new Error('Project membership rollout marker is missing or invalid') + return row.phase as ProjectMembershipPhase +} + +/** Mutating maintenance never writes columns while compatible applications still use the connector. */ +export async function assertProjectColumnAuthority(sql: Sql | TransactionSql): Promise { + if ((await readProjectMembershipPhase(sql)) !== 'column') + throw new Error( + 'Project membership authority must switch through db:migrate after verified server/worker drainage before maintenance writes' + ) +} + +/** Commits the forward-only authority change before any discovery, backfill, or repair. */ +export async function switchProjectMembershipAuthority(sql: Sql): Promise { + for (let attempt = 0; ; attempt++) { + try { + await sql.begin(async (tx) => { + await tx`SET LOCAL statement_timeout = '3s'` + await tx`SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL + THEN 'idle_in_transaction_session_timeout' ELSE 'transaction_timeout' END, '5s', true)` + await tx`LOCK TABLE public.workspace IN ACCESS EXCLUSIVE MODE NOWAIT` + const [operator] = await tx`SELECT EXISTS (SELECT 1 FROM pg_locks + WHERE pid = pg_backend_pid() AND locktype = 'advisory' AND granted + AND classid = ((hashtextextended('sim:project-backfill-operator',0) >> 32) & 4294967295)::oid + AND objid = (hashtextextended('sim:project-backfill-operator',0) & 4294967295)::oid + AND objsubid = 1) AS held` + if (!operator?.held) + throw new Error('Project authority switch requires the operator session lock') + const phase = await readProjectMembershipPhase(tx) + if (phase === 'column') return + const [state] = await tx`SELECT + to_regclass('public.project_workspace') IS NOT NULL AS connector, + EXISTS (SELECT 1 FROM public.workspace WHERE project_id IS NOT NULL) AS populated` + if (!state?.connector || state.populated) + throw new Error( + 'Connector authority requires an intact connector and no populated Project columns; reconcile the rollout state before switching' + ) + await tx`UPDATE public.project_membership_rollout SET phase = 'column' WHERE id = 'membership'` + }) + return + } catch (error) { + const transient = getTransientDatabaseFailure(error) + if (attempt >= 2 || (transient !== 'capacity' && transient !== 'conflict')) throw error + await sleep(backoffWithJitter(attempt, null, { baseMs: 100, maxMs: 500 })) + } + } +} + +/** Only an empty final schema push may initialize a missing singleton directly in column mode. */ +export async function bootstrapProjectColumnAuthority(sql: Sql): Promise { + await sql.begin(async (tx) => { + await tx`SET LOCAL statement_timeout = '3s'` + await tx`LOCK TABLE public.workspace IN ACCESS EXCLUSIVE MODE NOWAIT` + const [state] = await tx`SELECT + to_regclass('public.project_workspace') IS NULL AS retired, + EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'public.workspace'::regclass + AND attname = 'project_id' AND attnotnull AND NOT attisdropped) AS required, + NOT EXISTS (SELECT 1 FROM public.workspace) AND NOT EXISTS (SELECT 1 FROM public.project) AS empty` + if (!state?.retired || !state.required) + throw new Error('Project authority bootstrap requires the fresh final schema') + const [table] = + await tx`SELECT to_regclass('public.project_membership_rollout') IS NOT NULL AS present` + if (!table?.present) + throw new Error('Project membership rollout table is missing from the final schema') + const rows = await tx`SELECT id, phase FROM public.project_membership_rollout` + if (!rows.length && state.empty) + await tx`INSERT INTO public.project_membership_rollout (id, phase) VALUES ('membership', 'column')` + await assertProjectColumnAuthority(tx) + }) +} diff --git a/packages/db/migrations/0406_project_membership_enforcement.sql b/packages/db/migrations/0406_project_membership_enforcement.sql index d0052e28e34..3ab644c49a4 100644 --- a/packages/db/migrations/0406_project_membership_enforcement.sql +++ b/packages/db/migrations/0406_project_membership_enforcement.sql @@ -1,3 +1,3 @@ --- The registered 0031_project_membership script backfills and verifies before installing +-- The registered 0031_project_membership script commits the column-authority switch, then backfills and verifies before installing -- the final constraints and retiring the connector. SQL migrations run before scripts. SELECT 1; diff --git a/packages/db/script-migrations/0031_project_membership.ts b/packages/db/script-migrations/0031_project_membership.ts index 0e6aa092366..c9b587cd5eb 100644 --- a/packages/db/script-migrations/0031_project_membership.ts +++ b/packages/db/script-migrations/0031_project_membership.ts @@ -10,6 +10,7 @@ import { } from '@sim/db/maintenance/project-backfill' import { enforceProjectMembership } from '@sim/db/maintenance/project-enforcement' import { countPendingProjectArchiveRepairs } from '@sim/db/maintenance/project-repairs' +import { switchProjectMembershipAuthority } from '@sim/db/maintenance/project-rollout' import type { ScriptMigration } from '@sim/db/script-migrations/types' import { createLogger } from '@sim/logger' import { describeError, getTransientDatabaseFailure } from '@sim/utils/errors' @@ -46,6 +47,8 @@ export const projectMembershipMigration: ScriptMigration = { } try { await assertProjectBackfillDatabase(sql, true) + await switchProjectMembershipAuthority(sql) + await assertSession() if (await countPendingProjectArchiveRepairs(sql)) { throw new Error( 'Project archive cleanup is incomplete; resume repair with the original manifest before retrying' diff --git a/packages/db/scripts/project-authority.integration.ts b/packages/db/scripts/project-authority.integration.ts new file mode 100644 index 00000000000..70613da9944 --- /dev/null +++ b/packages/db/scripts/project-authority.integration.ts @@ -0,0 +1,167 @@ +import { execFile } from 'node:child_process' +import { readFile } from 'node:fs/promises' +import { promisify } from 'node:util' +import { runScriptMigrations, scriptMigrations } from '@sim/db/script-migrations' +import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' +import { generateId } from '@sim/utils/id' +import postgres, { type Sql } from 'postgres' +import { describe, expect, it } from 'vitest' + +async function database(run: (sql: Sql, runner: Sql, url: string) => Promise) { + const admin = postgres(readTestDatabaseUrl(), { max: 1 }) + const name = `project_authority_test_${generateId().replaceAll('-', '')}` + const url = new URL(readTestDatabaseUrl()) + url.pathname = `/${name}` + await admin`CREATE DATABASE ${admin(name)}` + const sql = postgres(url.toString(), { max: 1, onnotice: () => {} }) + const runner = postgres(url.toString(), { max: 1, max_lifetime: null, onnotice: () => {} }) + try { + await sql.unsafe(` + CREATE TABLE "user" (id text PRIMARY KEY); + CREATE TABLE organization (id text PRIMARY KEY); + CREATE TABLE workspace (id text PRIMARY KEY, name text NOT NULL, owner_id text NOT NULL, + organization_id text, archived_at timestamp, forked_from_workspace_id text REFERENCES workspace(id)); + CREATE INDEX workspace_parent_idx ON workspace(forked_from_workspace_id); + CREATE TABLE workflow (id text PRIMARY KEY, workspace_id text REFERENCES workspace(id), archived_at timestamp); + INSERT INTO "user" VALUES ('owner'); + `) + await sql.unsafe( + await readFile(new URL('../migrations/0394_project_foundation.sql', import.meta.url), 'utf8') + ) + const expansion = await readFile( + new URL('../migrations/0405_workspace_project_column.sql', import.meta.url), + 'utf8' + ) + for (const statement of expansion.split('--> statement-breakpoint')) await sql.unsafe(statement) + await sql.unsafe(`CREATE TABLE IF NOT EXISTS project_membership_rollout ( + id text PRIMARY KEY CHECK (id = 'membership'), + phase text NOT NULL DEFAULT 'connector' CONSTRAINT project_membership_rollout_phase CHECK (phase IN ('connector', 'column')) + )`) + await sql`INSERT INTO project_membership_rollout (id, phase) VALUES ('membership', 'connector') ON CONFLICT DO NOTHING` + await sql`INSERT INTO project (id,name,owner_id) VALUES ('retained','Retained','owner')` + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('env','Environment','owner')` + await sql`INSERT INTO project_workspace (project_id,workspace_id) VALUES ('retained','env')` + await run(sql, runner, url.toString()) + } finally { + await Promise.all([sql.end({ timeout: 2 }), runner.end({ timeout: 2 })]) + await admin`DROP DATABASE ${admin(name)} WITH (FORCE)` + await admin.end() + } +} + +function migrate(sql: Sql) { + return runScriptMigrations( + sql, + scriptMigrations.filter((entry) => entry.name === '0031_project_membership') + ) +} + +async function phase(sql: Sql) { + return sql`SELECT phase FROM project_membership_rollout WHERE id = 'membership'` +} + +describe('Project membership authority cutover', () => { + it('leaves connector authority intact while a legacy transaction is active and switches after it commits', async () => { + await database(async (sql, runner) => { + await sql.begin(async (tx) => { + await tx`UPDATE workspace SET name = 'Legacy write' WHERE id = 'env'` + await expect(migrate(runner)).rejects.toMatchObject({ code: '55P03' }) + expect(await phase(tx)).toEqual([{ phase: 'connector' }]) + expect(await tx`SELECT project_id FROM workspace`).toEqual([{ project_id: null }]) + }) + await migrate(runner) + expect(await phase(sql)).toEqual([{ phase: 'column' }]) + expect(await sql`SELECT name,project_id FROM workspace`).toEqual([ + { name: 'Legacy write', project_id: 'retained' }, + ]) + expect(await sql`SELECT to_regclass('project_workspace') AS connector`).toEqual([ + { connector: null }, + ]) + }) + }) + + it('rejects acknowledged CLI writes before the authority switch', async () => { + await database(async (sql, _runner, url) => { + await expect( + promisify(execFile)( + 'bun', + [ + '--no-env-file', + 'scripts/backfill-projects.ts', + 'apply', + '--manifest', + '/nonexistent-project-plan.json', + '--report', + '/nonexistent-project-report.json', + '--ack-release-drained', + ], + { + cwd: new URL('../../../apps/sim/', import.meta.url), + env: { ...process.env, MIGRATION_DATABASE_URL: url }, + } + ) + ).rejects.toMatchObject({ code: 1, stderr: expect.stringContaining('authority must switch') }) + expect(await phase(sql)).toEqual([{ phase: 'connector' }]) + expect(await sql`SELECT project_id FROM workspace`).toEqual([{ project_id: null }]) + }) + }) + + it('keeps read-only connector transactions ahead of cutover', async () => { + await database(async (sql, runner) => { + await sql.begin('isolation level repeatable read read only', async (tx) => { + await tx`LOCK TABLE workspace IN ACCESS SHARE MODE` + expect(await phase(tx)).toEqual([{ phase: 'connector' }]) + await expect(migrate(runner)).rejects.toMatchObject({ code: '55P03' }) + expect(await phase(tx)).toEqual([{ phase: 'connector' }]) + }) + await migrate(runner) + expect(await phase(sql)).toEqual([{ phase: 'column' }]) + }) + }) + + it('commits the switch before a discovery failure and resumes with intervening column writes', async () => { + await database(async (sql, runner) => { + await sql`INSERT INTO project (id,name,owner_id) VALUES ('empty','Needs remediation','owner')` + await expect(migrate(runner)).rejects.toThrow('conflicts') + expect(await phase(sql)).toEqual([{ phase: 'column' }]) + expect( + await sql`SELECT name FROM script_migrations WHERE name = '0031_project_membership'` + ).toHaveLength(0) + await sql`INSERT INTO workspace (id,name,owner_id,project_id) VALUES ('new','Column writer','owner','empty')` + await migrate(runner) + await migrate(runner) + expect(await phase(sql)).toEqual([{ phase: 'column' }]) + expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual([ + { id: 'env', project_id: 'retained' }, + { id: 'new', project_id: 'empty' }, + ]) + expect( + await sql`SELECT name FROM script_migrations WHERE name = '0031_project_membership'` + ).toHaveLength(1) + }) + }) + + it.each(['missing-row', 'missing-table', 'invalid-phase', 'populated-column'] as const)( + 'fails closed for %s without copying membership or retiring the connector', + async (state) => { + await database(async (sql, runner) => { + if (state === 'missing-row') await sql`DELETE FROM project_membership_rollout` + if (state === 'missing-table') await sql`DROP TABLE project_membership_rollout` + if (state === 'invalid-phase') { + await sql`ALTER TABLE project_membership_rollout DROP CONSTRAINT project_membership_rollout_phase` + await sql`UPDATE project_membership_rollout SET phase = 'invalid'` + } + if (state === 'populated-column') await sql`UPDATE workspace SET project_id = 'retained'` + const before = await sql`SELECT id,project_id FROM workspace` + await expect(migrate(runner)).rejects.toThrow(/authority|rollout|populated/i) + expect(await sql`SELECT id,project_id FROM workspace`).toEqual(before) + expect(await sql`SELECT project_id,workspace_id FROM project_workspace`).toEqual([ + { project_id: 'retained', workspace_id: 'env' }, + ]) + expect( + await sql`SELECT name FROM script_migrations WHERE name = '0031_project_membership'` + ).toHaveLength(0) + }) + } + ) +}) diff --git a/packages/db/scripts/project-contract-required.ts b/packages/db/scripts/project-contract-required.ts index e25884cfb1b..a700030d3a9 100644 --- a/packages/db/scripts/project-contract-required.ts +++ b/packages/db/scripts/project-contract-required.ts @@ -1,3 +1,4 @@ +import { readProjectMembershipPhase } from '@sim/db/maintenance/project-rollout' import journal from '@sim/db/migrations/meta/_journal.json' import { projectMembershipMigration } from '@sim/db/script-migrations/0031_project_membership' import { createLogger } from '@sim/logger' @@ -37,6 +38,14 @@ try { if (!applied?.expanded || !applied.columnExists) { throw new Error('Deploy the workspace Project column expansion before enforcing membership') } + const phase = await readProjectMembershipPhase(sql) + if (phase === 'connector') { + const [state] = await sql`SELECT + to_regclass('public.project_workspace') IS NOT NULL AS connector, + EXISTS (SELECT 1 FROM public.workspace WHERE project_id IS NOT NULL) AS populated` + if (!state?.connector || state.populated) + throw new Error('Connector authority has inconsistent membership; reconcile before rollout') + } const [scripts] = await sql`SELECT to_regclass('public.script_migrations') IS NOT NULL AS present` if (scripts.present) { @@ -49,7 +58,7 @@ try { to_regclass('public.project_workspace') IS NULL AS retired, EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'workspace'::regclass AND attname = 'project_id' AND attnotnull AND NOT attisdropped) AS enforced` - if (!state.retired || !state.enforced) + if (phase !== 'column' || !state.retired || !state.enforced) throw new Error('Project migration receipt disagrees with the physical schema') } } diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 2986e514536..738d64d951a 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -54,6 +54,13 @@ async function database(run: (sql: Sql, url: string) => Promise) { await readFile(new URL('../migrations/0394_project_foundation.sql', import.meta.url), 'utf8') ) await applyMigration(sql, expansion) + /** These reconciliation fixtures model the already committed column-authority phase. */ + await sql.unsafe(`CREATE TABLE IF NOT EXISTS project_membership_rollout ( + id text PRIMARY KEY CHECK (id = 'membership'), + phase text NOT NULL DEFAULT 'connector' CONSTRAINT project_membership_rollout_phase CHECK (phase IN ('connector', 'column')) + )`) + await sql`INSERT INTO project_membership_rollout (id, phase) VALUES ('membership', 'column') + ON CONFLICT (id) DO UPDATE SET phase = 'column'` await run(sql, url.toString()) } finally { await sql.end({ timeout: 2 }) @@ -1044,6 +1051,15 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sql`INSERT INTO drizzle.__drizzle_migrations VALUES (${expanded.when})` await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('legacy','Legacy','owner')` expect((await run()).stdout.trim()).toBe('required=true') + await sql`DELETE FROM project_membership_rollout` + await expect(run()).rejects.toMatchObject({ code: 1 }) + await sql`INSERT INTO project_membership_rollout (id, phase) VALUES ('membership', 'connector')` + expect((await run()).stdout.trim()).toBe('required=true') + await sql`INSERT INTO project (id,name,owner_id) VALUES ('gate','Gate','owner')` + await sql`UPDATE workspace SET project_id = 'gate' WHERE id = 'legacy'` + await expect(run()).rejects.toMatchObject({ code: 1 }) + await sql`UPDATE workspace SET project_id = NULL WHERE id = 'legacy'` + await sql`DELETE FROM project WHERE id = 'gate'` await sql`ALTER TABLE workspace DROP COLUMN project_id CASCADE` await expect(run()).rejects.toMatchObject({ code: 1 }) await applyMigration(sql, expansion) diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index 939173115a3..4ab92c41929 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -223,7 +223,10 @@ export const knowledgeBases = pgTable('knowledge_base', { await sql`ALTER TABLE workspace ADD COLUMN project_id text` await sql`UPDATE workspace SET project_id = 'retained'` } - await schema(`export const projects = pgTable('project', { id: text('id').primaryKey() }) + await schema(`export const rollout = pgTable('project_membership_rollout', { + id: text('id').primaryKey(), phase: text('phase').notNull().default('connector'), +}, (t) => [check('project_membership_rollout_id_check', sql\`\${t.id} = 'membership'\`), check('project_membership_rollout_phase_check', sql\`\${t.phase} IN ('connector', 'column')\`)]) +export const projects = pgTable('project', { id: text('id').primaryKey() }) export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), projectId: text('project_id').notNull() })`) const result = runPush(['--force']) expect(result.error).toBeUndefined() @@ -256,7 +259,10 @@ export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), pr expect(result.error).toBeUndefined() expect(result.status, result.stdout + result.stderr).toBe(0) } - await schema(`export const projects = pgTable('project', { + await schema(`export const rollout = pgTable('project_membership_rollout', { + id: text('id').primaryKey(), phase: text('phase').notNull().default('connector'), +}, (t) => [check('project_membership_rollout_id_check', sql\`\${t.id} = 'membership'\`), check('project_membership_rollout_phase_check', sql\`\${t.phase} IN ('connector', 'column')\`)]) +export const projects = pgTable('project', { id: text('id').primaryKey(), name: text('name').notNull(), ownerId: text('owner_id').notNull(), organizationId: text('organization_id'), archivedAt: timestamp('archived_at'), updatedAt: timestamp('updated_at').notNull().defaultNow(), }) @@ -272,6 +278,9 @@ export const workflows = pgTable('workflow', { expect(first.error, first.stderr).toBeUndefined() expect(first.status, first.stdout + first.stderr).toBe(0) reconcileProjects() + expect(await sql`SELECT id, phase FROM project_membership_rollout`).toEqual([ + { id: 'membership', phase: 'column' }, + ]) await expect( sql`INSERT INTO project (id,name,owner_id) VALUES ('empty','Empty','owner')` ).rejects.toMatchObject({ code: '23514' }) @@ -283,6 +292,7 @@ export const workflows = pgTable('workflow', { expect(repeated.error, repeated.stderr).toBeUndefined() expect(repeated.status, repeated.stdout + repeated.stderr).toBe(0) reconcileProjects() + expect(await sql`SELECT phase FROM project_membership_rollout`).toEqual([{ phase: 'column' }]) expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual([ { id: 'fork', project_id: 'family' }, { id: 'root', project_id: 'family' }, diff --git a/packages/db/scripts/reconcile-project-membership.ts b/packages/db/scripts/reconcile-project-membership.ts index 77ae76bb7a3..10e98687f3b 100644 --- a/packages/db/scripts/reconcile-project-membership.ts +++ b/packages/db/scripts/reconcile-project-membership.ts @@ -1,4 +1,5 @@ import { enforceProjectMembership } from '@sim/db/maintenance/project-enforcement' +import { bootstrapProjectColumnAuthority } from '@sim/db/maintenance/project-rollout' import { createLogger } from '@sim/logger' import { getErrorMessage, getPostgresErrorCode } from '@sim/utils/errors' import postgres from 'postgres' @@ -24,6 +25,7 @@ try { } if (!process.argv.includes('--prepare') && state.workspace && state.project) { // Drizzle cannot express lifecycle triggers; fresh push uses the same enforcement as migrations. + await bootstrapProjectColumnAuthority(sql) await enforceProjectMembership(sql) logger.info('Project membership validation and lifecycle enforcement completed') } From ceb7cf35dd2343e56d10bb5ca8a1ddb0ce28035c Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 22:26:44 -0700 Subject: [PATCH 084/113] fix(projects): switch membership authority after compatible rollout --- .../ee/workspace-forking/lib/create-fork.ts | 12 +- .../__integration__/foundation.integration.ts | 245 ++++++++++++++---- .../lib/projects/application/authorization.ts | 7 +- apps/sim/lib/projects/environment-source.ts | 47 +++- apps/sim/lib/projects/membership.ts | 19 +- .../__integration__/fork-sync.integration.ts | 8 +- apps/sim/lib/workspaces/create.ts | 10 +- .../organization-workspaces.integration.ts | 2 + .../0405_workspace_project_column.sql | 22 +- .../db/migrations/meta/0405_snapshot.json | 35 +++ packages/db/schema.ts | 22 +- packages/db/scripts/push.integration.ts | 10 + .../workspace-project-column.integration.ts | 27 +- packages/testing/src/mocks/database.mock.ts | 4 + .../src/mocks/schema-tables.generated.ts | 1 + 15 files changed, 392 insertions(+), 79 deletions(-) diff --git a/apps/sim/ee/workspace-forking/lib/create-fork.ts b/apps/sim/ee/workspace-forking/lib/create-fork.ts index 13f386a6a54..0e3e36584d5 100644 --- a/apps/sim/ee/workspace-forking/lib/create-fork.ts +++ b/apps/sim/ee/workspace-forking/lib/create-fork.ts @@ -1,5 +1,5 @@ import { db } from '@sim/db' -import { permissions, workspace } from '@sim/db/schema' +import { permissions, projectWorkspace, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' import type { PermissionType } from '@sim/platform-authz/workspace' import { getErrorMessage } from '@sim/utils/errors' @@ -7,6 +7,7 @@ import { generateId } from '@sim/utils/id' import { and, eq } from 'drizzle-orm' import type { Workspace } from '@/lib/api/contracts/workspaces' import { enqueueOutboxEvent } from '@/lib/core/outbox/service' +import { getProjectMembershipPhase } from '@/lib/projects/environment-source' import { requireForkProject } from '@/lib/projects/membership' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { insertNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' @@ -210,6 +211,7 @@ export async function createFork(params: CreateForkParams): Promise { + const phase = await getProjectMembershipPhase(tx) await setForkLockTimeout(tx) if (admission) { await lockWorkspaceOperationRequest(tx, admission.workspaceId, admission.requestId) @@ -303,7 +305,7 @@ export async function createFork(params: CreateForkParams): Promise { +beforeEach(async () => { + await db + .update(projectMembershipRollout) + .set({ phase: 'column' }) + .where(eq(projectMembershipRollout.id, 'membership')) setProjectsEnabled(true) }) @@ -304,15 +313,18 @@ afterAll(async () => { }) describe('Project foundation at the database and application boundary', () => { - check( - 'workspace creation and fork/disconnect write only the column while APIs remain disabled', - async () => { + for (const phase of ['connector', 'column'] as const) { + check(`workspace creation and fork/disconnect use only ${phase} authority`, async () => { setProjectsEnabled(false) const f = await fixture(false, 1) + await db + .update(projectMembershipRollout) + .set({ phase }) + .where(eq(projectMembershipRollout.id, 'membership')) const source = await db.transaction((tx) => createWorkspaceInTransaction(tx, { userId: f.ownerId, - name: 'Legacy source', + name: 'Source', organizationId: null, observedOrganizationId: null, governingPermissionGroupOrganizationId: null, @@ -321,46 +333,161 @@ describe('Project foundation at the database and application boundary', () => { skipDefaultWorkflow: true, }) ) - const [sourceMembership] = await db - .select({ projectId: workspace.projectId }) - .from(workspace) - .where(eq(workspace.id, source.id)) - const [persistedSource] = await db.select().from(workspace).where(eq(workspace.id, source.id)) - expect(persistedSource.projectId).toBe(sourceMembership.projectId) const parent = await getWorkspaceWithOwner(source.id) if (!parent) throw new Error('Missing source fixture') const fork = await createFork({ source: parent, policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), userId: f.ownerId, - name: 'Legacy child', + name: 'Child', }) - const [forkMembership] = await db - .select({ projectId: workspace.projectId }) - .from(workspace) - .where(eq(workspace.id, fork.workspace.id)) - expect(forkMembership.projectId).toBe(sourceMembership.projectId) - const [persistedFork] = await db - .select() - .from(workspace) - .where(eq(workspace.id, fork.workspace.id)) - expect(persistedFork.projectId).toBe(sourceMembership.projectId) + async function membership(id: string) { + return db.transaction(async (tx) => { + const environments = await getProjectEnvironmentSource(tx) + const [row] = await tx + .select({ projectId: environments.projectId }) + .from(environments) + .where(eq(environments.id, id)) + return row?.projectId + }) + } + const sourceProject = await membership(source.id) + expect(sourceProject).toEqual(expect.any(String)) + expect(await membership(fork.workspace.id)).toBe(sourceProject) await unlinkForkEdge({ parentWorkspaceId: source.id, childWorkspaceId: fork.workspace.id }) - const [child] = await db.select().from(workspace).where(eq(workspace.id, fork.workspace.id)) - const [detachedMembership] = await db - .select({ projectId: workspace.projectId }) + const childProject = await membership(fork.workspace.id) + expect(childProject).toEqual(expect.any(String)) + expect(childProject).not.toBe(sourceProject) + const rows = await db + .select({ projectId: workspace.projectId, parent: workspace.forkedFromWorkspaceId }) .from(workspace) - .where(eq(workspace.id, child.id)) - expect(child.forkedFromWorkspaceId).toBeNull() - expect(child.projectId).toBe(detachedMembership.projectId) - expect(child.projectId).not.toBe(sourceMembership.projectId) + .where(inArray(workspace.id, [source.id, fork.workspace.id])) + expect(rows.every((row) => row.parent === null)).toBe(true) + const connectors = await db + .select({ id: projectWorkspace.workspaceId }) + .from(projectWorkspace) + .where(inArray(projectWorkspace.workspaceId, [source.id, fork.workspace.id])) + expect(connectors).toHaveLength(phase === 'connector' ? 2 : 0) expect( - await db - .select({ id: projectWorkspace.workspaceId }) - .from(projectWorkspace) - .where(inArray(projectWorkspace.workspaceId, [source.id, child.id])) - ).toEqual([]) - expect(await db.select().from(project).where(eq(project.ownerId, f.ownerId))).toHaveLength(3) + rows.every((row) => + phase === 'connector' ? row.projectId === null : row.projectId !== null + ) + ).toBe(true) + if (phase === 'connector') { + /** The old Project-first teardown must remove a newly created private environment. */ + await db.transaction(async (tx) => { + await tx.delete(projectWorkspace).where(eq(projectWorkspace.projectId, childProject!)) + await tx.delete(project).where(eq(project.id, childProject!)) + await tx.delete(workspace).where(eq(workspace.id, fork.workspace.id)) + }) + expect(await membership(fork.workspace.id)).toBeUndefined() + } + }) + } + + check('authority barrier excludes cutover until a connector transaction commits', async () => { + await db + .update(projectMembershipRollout) + .set({ phase: 'connector' }) + .where(eq(projectMembershipRollout.id, 'membership')) + const entered = createDeferred() + const release = createDeferred() + const reader = db.transaction(async (tx) => { + expect(await getProjectMembershipPhase(tx)).toBe('connector') + entered.resolve() + await release.promise + expect(await getProjectMembershipPhase(tx)).toBe('connector') + }) + try { + await entered.promise + await expect( + db.transaction(async (tx) => { + await tx.execute(sql`LOCK TABLE workspace IN ACCESS EXCLUSIVE MODE NOWAIT`) + await tx.update(projectMembershipRollout).set({ phase: 'column' }) + }) + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '55P03') + } finally { + release.resolve() + await reader + } + await db.transaction(async (tx) => { + await tx.execute(sql`LOCK TABLE workspace IN ACCESS EXCLUSIVE MODE NOWAIT`) + await tx.update(projectMembershipRollout).set({ phase: 'column' }) + }) + expect(await db.transaction((tx) => getProjectMembershipPhase(tx))).toBe('column') + }) + + check( + 'Project snapshot waits before its first SELECT and observes committed cutover', + async () => { + const f = await fixture(false, 1) + await useLegacyMemberships(f.ids, f.projectId) + await db.update(projectMembershipRollout).set({ phase: 'connector' }) + const entered = createDeferred() + const release = createDeferred() + const cutover = db.transaction(async (tx) => { + await tx.execute(sql`LOCK TABLE workspace IN ACCESS EXCLUSIVE MODE NOWAIT`) + const [backend] = await tx.execute<{ pid: number }>(sql`SELECT pg_backend_pid() AS pid`) + await tx.update(projectMembershipRollout).set({ phase: 'column' }) + await tx + .update(workspace) + .set({ projectId: f.projectId, name: 'Committed after cutover' }) + .where(eq(workspace.id, f.ids[0])) + await tx.delete(projectWorkspace).where(eq(projectWorkspace.workspaceId, f.ids[0])) + entered.resolve(backend.pid) + await release.promise + }) + let read: ReturnType | undefined + try { + const pid = await entered.promise + read = getProject.execute({ + principal: f.owner, + input: { projectId: f.projectId }, + request, + }) + await waitUntilBlockedBy(pid, 'LOCK TABLE') + } finally { + release.resolve() + await cutover + } + const result = await read! + expect(result.project.environments.map((row) => row.id)).toEqual(f.ids) + expect(result.project.environments[0].name).toBe('Committed after cutover') + } + ) + + check( + 'missing authority refuses membership reads and creation without partial writes', + async () => { + const f = await fixture(false, 1) + await db.delete(projectMembershipRollout) + try { + await expect( + getProject.execute({ principal: f.owner, input: { projectId: f.projectId }, request }) + ).rejects.toThrow('Project membership authority is missing') + await expect( + db.transaction((tx) => + createWorkspaceInTransaction(tx, { + userId: f.ownerId, + name: 'Refused', + organizationId: null, + observedOrganizationId: null, + governingPermissionGroupOrganizationId: null, + workspaceMode: 'personal', + billedAccountUserId: f.ownerId, + skipDefaultWorkflow: true, + }) + ) + ).rejects.toThrow('Project membership authority is missing') + expect( + await db + .select({ id: workspace.id }) + .from(workspace) + .where(eq(workspace.ownerId, f.ownerId)) + ).toHaveLength(1) + } finally { + await db.insert(projectMembershipRollout).values({ id: 'membership', phase: 'column' }) + } } ) @@ -1556,27 +1683,33 @@ describe('Project foundation at the database and application boundary', () => { } ) - check('legacy Project account preview and teardown preserve surviving environments', async () => { - const f = await fixture() - await useLegacyMemberships(f.ids, f.projectId) - if (!f.organizationId) throw new Error('Missing organization fixture') - await db.insert(member).values({ - id: generateId(), - organizationId: f.organizationId, - userId: f.teammateId, - role: 'admin', - createdAt: new Date(), - }) - expect(await getProjectAccountDeletionBlockers(f.ownerId, [])).toEqual([]) - await db.transaction(async (tx) => { - expect(await prepareProjectsForAccountDeletion(tx, f.ownerId, [])).toEqual([]) - }) - const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) - expect(record.ownerId).toBe(f.teammateId) - expect( - await db.select({ id: workspace.id }).from(workspace).where(inArray(workspace.id, f.ids)) - ).toHaveLength(2) - }) + for (const phase of ['connector', 'column'] as const) { + check( + `legacy Project account preview and teardown preserve surviving environments in ${phase} mode`, + async () => { + const f = await fixture() + await useLegacyMemberships(f.ids, f.projectId) + await db.update(projectMembershipRollout).set({ phase }) + if (!f.organizationId) throw new Error('Missing organization fixture') + await db.insert(member).values({ + id: generateId(), + organizationId: f.organizationId, + userId: f.teammateId, + role: 'admin', + createdAt: new Date(), + }) + expect(await getProjectAccountDeletionBlockers(f.ownerId, [])).toEqual([]) + await db.transaction(async (tx) => { + expect(await prepareProjectsForAccountDeletion(tx, f.ownerId, [])).toEqual([]) + }) + const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) + expect(record.ownerId).toBe(f.teammateId) + expect( + await db.select({ id: workspace.id }).from(workspace).where(inArray(workspace.id, f.ids)) + ).toHaveLength(2) + } + ) + } check( 'legacy detach assigns the whole subtree and stale connector rows cannot restore membership', diff --git a/apps/sim/lib/projects/application/authorization.ts b/apps/sim/lib/projects/application/authorization.ts index cf1b9e6ba21..5daa073a5a6 100644 --- a/apps/sim/lib/projects/application/authorization.ts +++ b/apps/sim/lib/projects/application/authorization.ts @@ -11,7 +11,10 @@ import { CAPABILITY_RULES, refuseCapability } from '@/lib/permission-groups/capa import { acquirePermissionGroupOrgLock } from '@/lib/permission-groups/locks' import { resolveVerifiedUserAccessControlContext } from '@/lib/permission-groups/resolve.server' import { type ProjectOperation, projectOperations } from '@/lib/projects/application/operations' -import { getProjectEnvironmentSource } from '@/lib/projects/environment-source' +import { + getProjectEnvironmentSource, + lockProjectMembershipBarrier, +} from '@/lib/projects/environment-source' import { lockProject } from '@/lib/projects/membership' const logger = createLogger('ProjectAuthorization') @@ -220,6 +223,7 @@ export async function authorizeProject( input: ProjectAuthorizationInput & { projectId: string }, mode: ProjectAccessMode ): Promise { + await lockProjectMembershipBarrier(tx) if (mode === 'hold') await lockProject(tx, input.projectId) const [record] = await tx.select().from(project).where(eq(project.id, input.projectId)).limit(1) if ( @@ -238,6 +242,7 @@ export async function authorizeProjectsForRead( principal: SessionPrincipal, projectIds: string[] ): Promise { + await lockProjectMembershipBarrier(tx) if (!projectIds.length) return [] const records = await tx .select() diff --git a/apps/sim/lib/projects/environment-source.ts b/apps/sim/lib/projects/environment-source.ts index eef59844b57..8a77c5cbf34 100644 --- a/apps/sim/lib/projects/environment-source.ts +++ b/apps/sim/lib/projects/environment-source.ts @@ -1,4 +1,4 @@ -import { projectWorkspace, workspace } from '@sim/db/schema' +import { projectMembershipRollout, projectWorkspace, workspace } from '@sim/db/schema' import { and, eq, exists, inArray, isNotNull, isNull, ne, sql } from 'drizzle-orm' import type { SubqueryWithSelection } from 'drizzle-orm/pg-core' import type { DbTransaction } from '@/lib/db/types' @@ -13,24 +13,46 @@ const fields = { parentId: workspace.forkedFromWorkspaceId, } -/** - * Columns win over legacy assignments. The workspace table lock protects the catalog - * check and fallback read from connector retirement until the caller commits. It is - * compatible with ordinary writes; retirement locks workspace and both FK-related - * tables up front with NOWAIT before dropping the connector. - */ -async function hasLegacyMemberships(tx: DbTransaction): Promise { +/** Must precede the first snapshot-producing statement in repeatable-read transactions. */ +export async function lockProjectMembershipBarrier(tx: DbTransaction): Promise { await tx.execute(sql`LOCK TABLE ${workspace} IN ACCESS SHARE MODE`) +} + +/** The barrier pins database authority through commit; never cache the phase between transactions. */ +export async function getProjectMembershipPhase( + tx: DbTransaction +): Promise<'connector' | 'column'> { + await lockProjectMembershipBarrier(tx) + const [state] = await tx + .select({ phase: projectMembershipRollout.phase }) + .from(projectMembershipRollout) + .where(eq(projectMembershipRollout.id, 'membership')) + if (state?.phase !== 'connector' && state?.phase !== 'column') { + throw new Error('Project membership authority is missing or invalid') + } + return state.phase +} + +/** Caller holds the workspace barrier against connector retirement. */ +async function hasLegacyMemberships(tx: DbTransaction): Promise { const [legacy] = await tx.execute<{ present: boolean }>( sql`SELECT to_regclass('public.project_workspace') IS NOT NULL AS present` ) return legacy?.present ?? false } -/** Every workspace appears once; a populated column supersedes its legacy assignment. */ +/** Connector authority during overlap; column-first with null fallback after durable cutover. */ export async function getProjectEnvironmentSource( tx: DbTransaction ): Promise> { + const phase = await getProjectMembershipPhase(tx) + if (phase === 'connector') { + return tx + .select({ ...fields, projectId: projectWorkspace.projectId }) + .from(workspace) + .leftJoin(projectWorkspace, eq(projectWorkspace.workspaceId, workspace.id)) + .as('project_environments') + } if (!(await hasLegacyMemberships(tx))) return tx.select(fields).from(workspace).as('project_environments') return tx @@ -56,7 +78,12 @@ export async function deleteObsoleteProjectMemberships( tx: DbTransaction, projectIds: string[] ): Promise { - if (!projectIds.length || !(await hasLegacyMemberships(tx))) return + if ( + !projectIds.length || + (await getProjectMembershipPhase(tx)) !== 'column' || + !(await hasLegacyMemberships(tx)) + ) + return await tx.delete(projectWorkspace).where( and( inArray(projectWorkspace.projectId, projectIds), diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index 338e4230f30..75863651dc8 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -1,4 +1,4 @@ -import { permissionGroup, project, workspace } from '@sim/db/schema' +import { permissionGroup, project, projectWorkspace, workspace } from '@sim/db/schema' import { getPostgresErrorCode } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { compareStrings, truncateAtCodePoint } from '@sim/utils/string' @@ -12,7 +12,11 @@ import { import { textArrayLiteral } from '@/lib/db/arrays' import type { DbTransaction } from '@/lib/db/types' import { acquirePermissionGroupOrgLock } from '@/lib/permission-groups/locks' -import { getProjectEnvironmentSource } from '@/lib/projects/environment-source' +import { + getProjectEnvironmentSource, + getProjectMembershipPhase, + lockProjectMembershipBarrier, +} from '@/lib/projects/environment-source' const PROJECT_LOCK_TIMEOUT_MS = 5_000 @@ -34,6 +38,7 @@ async function withProjectLockTimeout( message: string, acquire: () => Promise ): Promise { + await lockProjectMembershipBarrier(tx) const [setting] = await tx.execute<{ previous: string }>( sql`SELECT current_setting('lock_timeout') AS previous` ) @@ -130,6 +135,7 @@ export async function createProjectRecord( projectName?: string } ): Promise { + await getProjectMembershipPhase(tx) const id = generateId() await tx.insert(project).values({ id, @@ -286,7 +292,14 @@ export async function splitForkProject( .where(inArray(workspace.id, ids)) .orderBy(asc(workspace.id)) .for('no key update') - await tx.update(workspace).set({ projectId: id }).where(inArray(workspace.id, ids)) + if ((await getProjectMembershipPhase(tx)) === 'connector') { + await tx + .update(projectWorkspace) + .set({ projectId: id }) + .where(inArray(projectWorkspace.workspaceId, ids)) + } else { + await tx.update(workspace).set({ projectId: id }).where(inArray(workspace.id, ids)) + } }) if (owner.organizationId) { await acquirePermissionGroupOrgLock(tx, owner.organizationId) diff --git a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts index c52c8fcd0e4..24725b5dfdd 100644 --- a/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts +++ b/apps/sim/lib/workspaces/__integration__/fork-sync.integration.ts @@ -10,6 +10,7 @@ import { outboxEvent, permissions, project, + projectWorkspace, user, userTableDefinitions, workflow, @@ -29,6 +30,7 @@ import { withWorkspaceInvocationScope } from '@/lib/core/application/workspace-i import { processOutboxEventById } from '@/lib/core/outbox/service' import * as workflowMcpSync from '@/lib/mcp/workflow-mcp-sync' import { createScopedCliTransport } from '@/lib/mothership/agent-cli/scoped-transport' +import { getProjectMembershipPhase } from '@/lib/projects/environment-source' import { createProjectRecord } from '@/lib/projects/membership' import { readWorkflowVersion } from '@/lib/workflows/application/read-workflow-version' import { workflowDeploymentOutboxHandlers } from '@/lib/workflows/deployment-outbox' @@ -147,6 +149,7 @@ describe('authorized fork and sync against PostgreSQL', () => { updatedAt: now, }) await db.transaction(async (tx) => { + const phase = await getProjectMembershipPhase(tx) const projectId = await createProjectRecord(tx, { name: 'Fork source fixture', ownerId: userId, @@ -154,12 +157,15 @@ describe('authorized fork and sync against PostgreSQL', () => { }) await tx.insert(workspace).values({ id: sourceWorkspaceId, - projectId, + projectId: phase === 'column' ? projectId : null, name: 'Fork source fixture', ownerId: userId, billedAccountUserId: userId, allowPersonalApiKeys: true, }) + if (phase === 'connector') { + await tx.insert(projectWorkspace).values({ projectId, workspaceId: sourceWorkspaceId }) + } }) await db.insert(permissions).values({ id: generateId(), diff --git a/apps/sim/lib/workspaces/create.ts b/apps/sim/lib/workspaces/create.ts index ab8d951af88..35e467f3639 100644 --- a/apps/sim/lib/workspaces/create.ts +++ b/apps/sim/lib/workspaces/create.ts @@ -1,10 +1,11 @@ import { db } from '@sim/db' -import { permissions, type WorkspaceMode, workspace } from '@sim/db/schema' +import { permissions, projectWorkspace, type WorkspaceMode, workspace } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { getPostgresConstraintName, getPostgresErrorCode } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { PlatformEvents } from '@/lib/core/telemetry' import type { DbTransaction } from '@/lib/db/types' +import { getProjectMembershipPhase } from '@/lib/projects/environment-source' import { createProjectRecord } from '@/lib/projects/membership' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { insertNewWorkflowRow } from '@/lib/workflows/persistence/new-workflow-row' @@ -106,6 +107,7 @@ export async function createWorkspaceWithProjectInTransaction( governingPermissionGroupOrganizationId, }: TransactionalCreateWorkspaceParams & { projectName?: string } ): Promise<{ projectId: string; workspace: CreatedWorkspace }> { + const phase = await getProjectMembershipPhase(tx) const workspaceId = generateId() const workflowId = generateId() const now = new Date() @@ -131,7 +133,7 @@ export async function createWorkspaceWithProjectInTransaction( await tx.insert(workspace).values({ id: workspaceId, - projectId, + projectId: phase === 'column' ? projectId : null, name, ownerId: userId, organizationId, @@ -142,6 +144,10 @@ export async function createWorkspaceWithProjectInTransaction( updatedAt: now, }) + if (phase === 'connector') { + await tx.insert(projectWorkspace).values({ projectId, workspaceId }) + } + const permissionRows = [ { id: generateId(), diff --git a/apps/sim/lib/workspaces/organization-workspaces.integration.ts b/apps/sim/lib/workspaces/organization-workspaces.integration.ts index 805331de6e0..ff18f1315e8 100644 --- a/apps/sim/lib/workspaces/organization-workspaces.integration.ts +++ b/apps/sim/lib/workspaces/organization-workspaces.integration.ts @@ -33,6 +33,8 @@ beforeAll(async () => { organization_id text REFERENCES organization(id) ON DELETE RESTRICT, updated_at timestamp ); + CREATE TABLE project_membership_rollout (id text PRIMARY KEY, phase text NOT NULL); + INSERT INTO project_membership_rollout VALUES ('membership', 'column'); CREATE TABLE permission_group ( id text PRIMARY KEY, organization_id text, config jsonb, updated_at timestamp ); diff --git a/packages/db/migrations/0405_workspace_project_column.sql b/packages/db/migrations/0405_workspace_project_column.sql index a7d90ceec3a..3041e5819fb 100644 --- a/packages/db/migrations/0405_workspace_project_column.sql +++ b/packages/db/migrations/0405_workspace_project_column.sql @@ -6,7 +6,27 @@ SELECT set_config(CASE WHEN current_setting('transaction_timeout', true) IS NULL SET LOCAL statement_timeout = '5s'; LOCK TABLE workspace IN ACCESS EXCLUSIVE MODE NOWAIT; LOCK TABLE project IN SHARE ROW EXCLUSIVE MODE NOWAIT; -ALTER TABLE "workspace" ADD COLUMN IF NOT EXISTS "project_id" text; +CREATE TABLE IF NOT EXISTS "project_membership_rollout" ( + "id" text PRIMARY KEY, + "phase" text DEFAULT 'connector' NOT NULL, + CONSTRAINT "project_membership_rollout_singleton" CHECK ("id" = 'membership'), + CONSTRAINT "project_membership_rollout_phase" CHECK ("phase" IN ('connector', 'column')) +); +DO $$ +DECLARE had_column boolean; +BEGIN + SELECT EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'workspace'::regclass + AND attname = 'project_id' AND NOT attisdropped) INTO had_column; + ALTER TABLE workspace ADD COLUMN IF NOT EXISTS project_id text; + IF NOT EXISTS (SELECT 1 FROM project_membership_rollout WHERE id = 'membership') THEN + IF had_column THEN + IF EXISTS (SELECT 1 FROM workspace WHERE project_id IS DISTINCT FROM NULL) THEN + RAISE EXCEPTION 'Populated Project columns require explicit authority reconciliation before rollout initialization' USING ERRCODE = '55000'; + END IF; + END IF; + INSERT INTO project_membership_rollout (id, phase) VALUES ('membership', 'connector'); + END IF; +END $$; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'workspace'::regclass AND conname = 'workspace_project_id_project_id_fk') THEN diff --git a/packages/db/migrations/meta/0405_snapshot.json b/packages/db/migrations/meta/0405_snapshot.json index f12328a6f30..06ef16a4259 100644 --- a/packages/db/migrations/meta/0405_snapshot.json +++ b/packages/db/migrations/meta/0405_snapshot.json @@ -30614,6 +30614,41 @@ "policies": {}, "checkConstraints": {}, "isRLSEnabled": false + }, + "public.project_membership_rollout": { + "name": "project_membership_rollout", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'connector'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "project_membership_rollout_singleton": { + "name": "project_membership_rollout_singleton", + "value": "\"project_membership_rollout\".\"id\" = 'membership'" + }, + "project_membership_rollout_phase": { + "name": "project_membership_rollout_phase", + "value": "\"project_membership_rollout\".\"phase\" IN ('connector', 'column')" + } + }, + "isRLSEnabled": false } }, "enums": { diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 645039139d8..b3ff602c3ef 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2113,12 +2113,30 @@ export const project = pgTable( }) ) +/** Durable authority selection; retained after connector retirement for expansion rollback. */ +export const projectMembershipRollout = pgTable( + 'project_membership_rollout', + { + id: text('id').primaryKey(), + phase: text('phase', { enum: ['connector', 'column'] }) + .notNull() + .default('connector'), + }, + (table) => ({ + singleton: check('project_membership_rollout_singleton', sql`${table.id} = 'membership'`), + phase: check( + 'project_membership_rollout_phase', + sql`${table.phase} IN ('connector', 'column')` + ), + }) +) + // contract-pending(after project writers are fully deployed and backfill validates): enforce exactly-one membership and active Project environment minimums at commit. export const projectWorkspace = pgTable( 'project_workspace', { - // contract-pending(after #8830 is fully deployed and pre-8830 servers/workers drain): retire this table in #8590 — new writes use workspace.projectId and compatibility readers tolerate its absence. - /** @deprecated Use workspace.projectId; retained for pre-8830 binaries during rollout. */ + // contract-pending(after #8830 is fully deployed and pre-8830 servers/workers drain): retire this table in #8590 — the durable authority marker has switched to column and compatibility readers tolerate its absence. + /** @deprecated Use the phase-aware membership helpers; retained for pre-8830 binaries during rollout. */ projectId: text('project_id') .notNull() .references(() => project.id, { onDelete: 'restrict' }), diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index a0d60010c3c..c5f6eb49504 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -196,6 +196,9 @@ export const knowledgeBases = pgTable('knowledge_base', { export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), forkedFromWorkspaceId: text('forked_from_workspace_id'), projectId: text('project_id'), }, (table) => [index('workspace_project_id_id_idx').on(table.projectId, table.id).concurrently()]) +export const rollout = pgTable('project_membership_rollout', { + id: text('id').primaryKey(), phase: text('phase').notNull().default('connector'), +}, (table) => [check('project_membership_rollout_singleton', sql\`\${table.id} = 'membership'\`), check('project_membership_rollout_phase', sql\`\${table.phase} IN ('connector', 'column')\`)]) export const memberships = pgTable('project_workspace', { projectId: text('project_id').notNull(), workspaceId: text('workspace_id').notNull().unique(), })`) @@ -210,6 +213,10 @@ export const memberships = pgTable('project_workspace', { { id: 'root', project_id: null }, { id: 'standalone', project_id: null }, ]) + expect(await sql`SELECT phase FROM project_membership_rollout`).toEqual([ + { phase: 'connector' }, + ]) + await sql`UPDATE project_membership_rollout SET phase = 'column' WHERE id = 'membership'` await sql`UPDATE project_workspace SET project_id = 'singleton' WHERE workspace_id = 'fork'` await sql`UPDATE workspace SET project_id = 'family' WHERE id = 'standalone'` const indexBeforeReplay = await sql`SELECT indexrelid, indisvalid FROM pg_index @@ -217,6 +224,7 @@ export const memberships = pgTable('project_workspace', { expect(indexBeforeReplay).toEqual([{ indexrelid: expect.any(Number), indisvalid: true }]) const repeated = runPush(['--force']) expect(repeated.error, repeated.stderr).toBeUndefined() + expect(await sql`SELECT phase FROM project_membership_rollout`).toEqual([{ phase: 'column' }]) expect( await sql`SELECT indexrelid, indisvalid FROM pg_index WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` @@ -236,6 +244,8 @@ export const memberships = pgTable('project_workspace', { await sql`CREATE TABLE workspace (id text PRIMARY KEY, project_id text)` await sql`CREATE TABLE project_workspace (project_id text NOT NULL, workspace_id text NOT NULL UNIQUE)` await sql`INSERT INTO project VALUES ('family')` + await sql`CREATE TABLE project_membership_rollout (id text PRIMARY KEY, phase text NOT NULL)` + await sql`INSERT INTO project_membership_rollout VALUES ('membership', 'column')` await sql`INSERT INTO workspace VALUES ('root', 'family'), ('fork', 'family')` await expect( sql`CREATE UNIQUE INDEX CONCURRENTLY workspace_project_id_id_idx ON workspace(project_id)` diff --git a/packages/db/scripts/workspace-project-column.integration.ts b/packages/db/scripts/workspace-project-column.integration.ts index f845f7a2fde..723bc757028 100644 --- a/packages/db/scripts/workspace-project-column.integration.ts +++ b/packages/db/scripts/workspace-project-column.integration.ts @@ -34,6 +34,9 @@ describe('workspace Project column compatibility migration', () => { it('leaves legacy rows unassigned and accepts old workspace-before-Project creation', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) + expect(await sql`SELECT id, phase FROM project_membership_rollout`).toEqual([ + { id: 'membership', phase: 'connector' }, + ]) await sql.begin(async (tx) => { await tx`INSERT INTO workspace (id) VALUES ('old-writer')` await tx`INSERT INTO project VALUES ('new-project')` @@ -54,6 +57,7 @@ describe('workspace Project column compatibility migration', () => { it('new column writes do not create or overwrite legacy connector assignments', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) + await sql`UPDATE project_membership_rollout SET phase = 'column' WHERE id = 'membership'` await sql`INSERT INTO workspace (id, project_id) VALUES ('new-writer', 'private')` await sql`UPDATE workspace SET project_id = 'private', forked_from_workspace_id = NULL WHERE id = 'staging'` @@ -71,9 +75,10 @@ describe('workspace Project column compatibility migration', () => { }) }) - it('rejects dangling new assignments and protects column-only Projects from old teardown', async () => { + it('rejects dangling assignments and deletion of Projects with surviving column memberships', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) + await sql`UPDATE project_membership_rollout SET phase = 'column' WHERE id = 'membership'` await expect( sql`INSERT INTO workspace (id, project_id) VALUES ('invalid', 'missing')` ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '23503') @@ -112,10 +117,12 @@ describe('workspace Project column compatibility migration', () => { it('replays without copying stale legacy assignments over columns or detached forks', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) + await sql`UPDATE project_membership_rollout SET phase = 'column' WHERE id = 'membership'` await sql`UPDATE workspace SET project_id = 'private', forked_from_workspace_id = NULL WHERE id = 'staging'` await applyMigration(sql, migration) await applyMigration(sql, migration) + expect(await sql`SELECT phase FROM project_membership_rollout`).toEqual([{ phase: 'column' }]) expect( await sql`SELECT w.id, w.project_id, pw.project_id AS legacy, w.forked_from_workspace_id AS parent FROM workspace w @@ -128,6 +135,23 @@ describe('workspace Project column compatibility migration', () => { }) }) + it('refuses to initialize missing authority over populated columns', async () => { + await fixture(async (sql) => { + await sql`ALTER TABLE workspace ADD COLUMN project_id text` + await sql`UPDATE workspace SET project_id = 'family' WHERE id = 'production'` + await expect(applyMigration(sql, migration)).rejects.toSatisfy( + (error: unknown) => getPostgresErrorCode(error) === '55000' + ) + await sql.unsafe('ROLLBACK') + expect(await sql`SELECT project_id FROM workspace WHERE id = 'production'`).toEqual([ + { project_id: 'family' }, + ]) + expect(await sql`SELECT to_regclass('project_membership_rollout') AS marker`).toEqual([ + { marker: null }, + ]) + }) + }) + it('refuses a busy expansion immediately without blocking unrelated writes', async () => { await fixture(async (sql) => { const [scope] = await sql<{ schema: string }[]>`SELECT current_schema() AS schema` @@ -155,6 +179,7 @@ describe('workspace Project column compatibility migration', () => { it('repairs an interrupted index build without changing assignments', async () => { await fixture(async (sql) => { await applyMigration(sql, migration) + await sql`UPDATE project_membership_rollout SET phase = 'column' WHERE id = 'membership'` await sql`UPDATE workspace SET project_id = 'family' WHERE id IN ('production', 'staging')` await sql`DROP INDEX CONCURRENTLY workspace_project_id_id_idx` await expect( diff --git a/packages/testing/src/mocks/database.mock.ts b/packages/testing/src/mocks/database.mock.ts index 14cd6f2fc37..9c4657b58ed 100644 --- a/packages/testing/src/mocks/database.mock.ts +++ b/packages/testing/src/mocks/database.mock.ts @@ -151,6 +151,10 @@ function dequeueChainRows(tables: unknown[]): unknown[] | null { const queue = tableRowQueues.get(table) if (queue && queue.length > 0) return queue.shift() ?? null } + /** Default unit fixtures represent a migrated database after authority cutover. */ + if (tables.includes(schemaMock.projectMembershipRollout)) { + return [{ id: 'membership', phase: 'column' }] + } return null } diff --git a/packages/testing/src/mocks/schema-tables.generated.ts b/packages/testing/src/mocks/schema-tables.generated.ts index 98a76455b3d..22cce1a5b48 100644 --- a/packages/testing/src/mocks/schema-tables.generated.ts +++ b/packages/testing/src/mocks/schema-tables.generated.ts @@ -504,6 +504,7 @@ export const GENERATED_SCHEMA_TABLES = { 'updatedAt', ], project: ['id', 'name', 'organizationId', 'ownerId', 'archivedAt', 'createdAt', 'updatedAt'], + projectMembershipRollout: ['id', 'phase'], projectWorkspace: ['projectId', 'workspaceId', 'createdAt'], workspaceForkResourceMap: [ 'id', From 8a3993fab11a0b5eec5d9a0ff014ac48970b8d06 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 22:45:27 -0700 Subject: [PATCH 085/113] test(projects): guard optional authority fixtures explicitly --- .../projects/__integration__/foundation.integration.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 933e1336d73..9fd90ea802b 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -358,6 +358,7 @@ describe('Project foundation at the database and application boundary', () => { const childProject = await membership(fork.workspace.id) expect(childProject).toEqual(expect.any(String)) expect(childProject).not.toBe(sourceProject) + if (!childProject) throw new Error('Detached environment is missing its Project') const rows = await db .select({ projectId: workspace.projectId, parent: workspace.forkedFromWorkspaceId }) .from(workspace) @@ -376,8 +377,8 @@ describe('Project foundation at the database and application boundary', () => { if (phase === 'connector') { /** The old Project-first teardown must remove a newly created private environment. */ await db.transaction(async (tx) => { - await tx.delete(projectWorkspace).where(eq(projectWorkspace.projectId, childProject!)) - await tx.delete(project).where(eq(project.id, childProject!)) + await tx.delete(projectWorkspace).where(eq(projectWorkspace.projectId, childProject)) + await tx.delete(project).where(eq(project.id, childProject)) await tx.delete(workspace).where(eq(workspace.id, fork.workspace.id)) }) expect(await membership(fork.workspace.id)).toBeUndefined() @@ -450,7 +451,8 @@ describe('Project foundation at the database and application boundary', () => { release.resolve() await cutover } - const result = await read! + if (!read) throw new Error('Project snapshot read was not started') + const result = await read expect(result.project.environments.map((row) => row.id)).toEqual(f.ids) expect(result.project.environments[0].name).toBe('Committed after cutover') } From 9edfcde87b8790753711f26fcb15a4cc2eb59a12 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 22:49:50 -0700 Subject: [PATCH 086/113] test(projects): seed authority in billing lock-order mock --- apps/sim/lib/billing/organizations/lock-order.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/sim/lib/billing/organizations/lock-order.test.ts b/apps/sim/lib/billing/organizations/lock-order.test.ts index de3c565da7b..3b8819eb326 100644 --- a/apps/sim/lib/billing/organizations/lock-order.test.ts +++ b/apps/sim/lib/billing/organizations/lock-order.test.ts @@ -11,6 +11,7 @@ import { organization, outboxEvent, permissions, + projectMembershipRollout, subscription as subscriptionTable, user, userStats, @@ -182,6 +183,7 @@ describe('workspace payer-change transaction lock ordering', () => { const ops: Array<{ op: 'lock' | 'payer-transfer' | 'update'; table: unknown }> = [] let memberSelectCount = 0 const rowsForTable = (table: unknown, fields?: Record): unknown[] => { + if (table === projectMembershipRollout) return [{ phase: 'column' }] if (table === workspace) { if (fields?.id === workspace.projectId) return [{ id: null }] return [ From 985659572e826115861c0eeffbe39091ee7726d4 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 23:03:41 -0700 Subject: [PATCH 087/113] fix(projects): bound authority barriers and verify push replay --- .../__integration__/foundation.integration.ts | 42 +++++++++++++++++++ apps/sim/lib/projects/account-deletion.ts | 7 +--- apps/sim/lib/projects/environment-source.ts | 20 ++++++++- apps/sim/lib/projects/errors.ts | 9 ++++ apps/sim/lib/projects/membership.ts | 10 +---- apps/sim/lib/workspaces/admin-move.ts | 3 +- packages/db/scripts/push.integration.ts | 8 ++++ 7 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 apps/sim/lib/projects/errors.ts diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index 9fd90ea802b..e1292b3f273 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -51,6 +51,7 @@ import { getProjectEnvironmentSource, getProjectMembershipPhase, } from '@/lib/projects/environment-source' +import { ProjectConflictError } from '@/lib/projects/errors' import { archiveProjectInTransaction } from '@/lib/projects/lifecycle' import { createProjectRecord, @@ -386,6 +387,47 @@ describe('Project foundation at the database and application boundary', () => { }) } + check( + 'authority contention is bounded and maps to a Project conflict for reads and writers', + async () => { + const f = await fixture(false, 1) + const entered = createDeferred() + const release = createDeferred() + const holder = db.transaction(async (tx) => { + await tx.execute(sql`LOCK TABLE workspace IN ACCESS EXCLUSIVE MODE NOWAIT`) + entered.resolve() + await release.promise + }) + try { + await entered.promise + await Promise.all( + [ + db.transaction( + async (tx) => { + await tx.execute(sql`SET LOCAL statement_timeout = '7s'`) + await getProjectMembershipPhase(tx) + }, + { isolationLevel: 'repeatable read', accessMode: 'read only' } + ), + db.transaction(async (tx) => { + await tx.execute(sql`SET LOCAL statement_timeout = '7s'`) + await lockProject(tx, f.projectId) + }), + ].map((operation) => expect(operation).rejects.toBeInstanceOf(ProjectConflictError)) + ) + } finally { + release.resolve() + await holder + } + await db.transaction(async (tx) => { + await tx.execute(sql`SET LOCAL lock_timeout = '1234ms'`) + expect(await getProjectMembershipPhase(tx)).toBe('column') + const [setting] = await tx.execute<{ lock_timeout: string }>(sql`SHOW lock_timeout`) + expect(setting.lock_timeout).toBe('1234ms') + }) + } + ) + check('authority barrier excludes cutover until a connector transaction commits', async () => { await db .update(projectMembershipRollout) diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts index 389ff4e1d30..07ec1ec61ae 100644 --- a/apps/sim/lib/projects/account-deletion.ts +++ b/apps/sim/lib/projects/account-deletion.ts @@ -7,11 +7,8 @@ import { deleteObsoleteProjectMemberships, getProjectEnvironmentSource, } from '@/lib/projects/environment-source' -import { - lockProjectBackfillWrites, - lockProjects, - ProjectConflictError, -} from '@/lib/projects/membership' +import { ProjectConflictError } from '@/lib/projects/errors' +import { lockProjectBackfillWrites, lockProjects } from '@/lib/projects/membership' /** Two indexed lookups; an `OR` around a membership subquery would scan every Project. */ async function loadRelatedProjects( diff --git a/apps/sim/lib/projects/environment-source.ts b/apps/sim/lib/projects/environment-source.ts index 8a77c5cbf34..e23d5caa1d5 100644 --- a/apps/sim/lib/projects/environment-source.ts +++ b/apps/sim/lib/projects/environment-source.ts @@ -1,7 +1,9 @@ import { projectMembershipRollout, projectWorkspace, workspace } from '@sim/db/schema' +import { getPostgresErrorCode } from '@sim/utils/errors' import { and, eq, exists, inArray, isNotNull, isNull, ne, sql } from 'drizzle-orm' import type { SubqueryWithSelection } from 'drizzle-orm/pg-core' import type { DbTransaction } from '@/lib/db/types' +import { ProjectConflictError } from '@/lib/projects/errors' const fields = { id: workspace.id, @@ -13,9 +15,23 @@ const fields = { parentId: workspace.forkedFromWorkspaceId, } -/** Must precede the first snapshot-producing statement in repeatable-read transactions. */ +/** + * Bound every authority wait without establishing a repeatable-read snapshot before the lock. + * SHOW and SET are utility statements; restore the caller's setting only after acquiring it. + */ export async function lockProjectMembershipBarrier(tx: DbTransaction): Promise { - await tx.execute(sql`LOCK TABLE ${workspace} IN ACCESS SHARE MODE`) + const [setting] = await tx.execute<{ lock_timeout: string }>(sql`SHOW lock_timeout`) + await tx.execute(sql`SET LOCAL lock_timeout = '5s'`) + try { + await tx.execute(sql`LOCK TABLE ${workspace} IN ACCESS SHARE MODE`) + } catch (error) { + const code = getPostgresErrorCode(error) + if (code === '55P03' || code === '40P01') { + throw new ProjectConflictError('Project membership is changing; retry the operation') + } + throw error + } + await tx.execute(sql`SELECT set_config('lock_timeout', ${setting?.lock_timeout ?? '0'}, true)`) } /** The barrier pins database authority through commit; never cache the phase between transactions. */ diff --git a/apps/sim/lib/projects/errors.ts b/apps/sim/lib/projects/errors.ts new file mode 100644 index 00000000000..29887e13f32 --- /dev/null +++ b/apps/sim/lib/projects/errors.ts @@ -0,0 +1,9 @@ +import { OrchestrationError } from '@/lib/core/orchestration/types' + +/** A Project lifecycle rule or lock refused the change; callers may map it to their own error. */ +export class ProjectConflictError extends OrchestrationError { + constructor(message: string) { + super('conflict', message) + this.name = 'ProjectConflictError' + } +} diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index 75863651dc8..17c93c13732 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -3,7 +3,6 @@ import { getPostgresErrorCode } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { compareStrings, truncateAtCodePoint } from '@sim/utils/string' import { and, asc, eq, inArray, isNotNull, isNull, notInArray, type SQL, sql } from 'drizzle-orm' -import { OrchestrationError } from '@/lib/core/orchestration/types' import { acquireAdvisoryXactLock, acquireAdvisoryXactLocks, @@ -17,17 +16,10 @@ import { getProjectMembershipPhase, lockProjectMembershipBarrier, } from '@/lib/projects/environment-source' +import { ProjectConflictError } from '@/lib/projects/errors' const PROJECT_LOCK_TIMEOUT_MS = 5_000 -/** A Project lifecycle rule or lock refused the change; callers may map it to their own error. */ -export class ProjectConflictError extends OrchestrationError { - constructor(message: string) { - super('conflict', message) - this.name = 'ProjectConflictError' - } -} - /** * Waits in `acquire` are bounded by {@link PROJECT_LOCK_TIMEOUT_MS} and a timeout or * deadlock becomes a retryable Project conflict. The caller's own `lock_timeout` is diff --git a/apps/sim/lib/workspaces/admin-move.ts b/apps/sim/lib/workspaces/admin-move.ts index 6c3f9f6f86a..df884175df0 100644 --- a/apps/sim/lib/workspaces/admin-move.ts +++ b/apps/sim/lib/workspaces/admin-move.ts @@ -41,7 +41,8 @@ import type { DbOrTx } from '@/lib/db/types' import { getInvitationById, isInvitationExpired } from '@/lib/invitations/core' import { acquireInvitationMutationLocks } from '@/lib/invitations/locks' import { PENDING_INVITATION_UNIQUE_INDEX, sendInvitationEmail } from '@/lib/invitations/send' -import { ProjectConflictError, transferWorkspaceProjects } from '@/lib/projects/membership' +import { ProjectConflictError } from '@/lib/projects/errors' +import { transferWorkspaceProjects } from '@/lib/projects/membership' import { invalidateWorkspaceTableLimitsCache } from '@/lib/table/billing' import { deleteCustomBlock } from '@/lib/workflows/custom-blocks/operations' import { MIGRATED_INVITATION_EMAIL_EVENT_TYPE } from '@/lib/workspaces/admin-move-event' diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index c5f6eb49504..f0d51756eda 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -222,8 +222,16 @@ export const memberships = pgTable('project_workspace', { const indexBeforeReplay = await sql`SELECT indexrelid, indisvalid FROM pg_index WHERE indexrelid = 'workspace_project_id_id_idx'::regclass` expect(indexBeforeReplay).toEqual([{ indexrelid: expect.any(Number), indisvalid: true }]) + /** Replay must restore real DDL, not pass from unchanged state after an early child failure. */ + await sql`ALTER TABLE workspace DROP CONSTRAINT workspace_project_id_project_id_fk` const repeated = runPush(['--force']) expect(repeated.error, repeated.stderr).toBeUndefined() + /** This partial fixture reaches Project reconciliation, then lacks the next reconciler's tables. */ + expect(repeated.status, repeated.stdout + repeated.stderr).toBe(1) + expect( + await sql`SELECT confdeltype, convalidated FROM pg_constraint + WHERE conrelid = 'workspace'::regclass AND conname = 'workspace_project_id_project_id_fk'` + ).toEqual([{ confdeltype: 'r', convalidated: false }]) expect(await sql`SELECT phase FROM project_membership_rollout`).toEqual([{ phase: 'column' }]) expect( await sql`SELECT indexrelid, indisvalid FROM pg_index From 357740aae007c01218c346dbcd8a9e20cef24b57 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Fri, 9 Oct 2026 23:18:23 -0700 Subject: [PATCH 088/113] fix(projects): pin operator schema and pace detach repairs --- apps/sim/scripts/backfill-projects.ts | 5 +- .../scripts/project-contract.integration.ts | 61 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/apps/sim/scripts/backfill-projects.ts b/apps/sim/scripts/backfill-projects.ts index 2c03c4321e4..82a9cf46e76 100644 --- a/apps/sim/scripts/backfill-projects.ts +++ b/apps/sim/scripts/backfill-projects.ts @@ -204,8 +204,10 @@ The registered migration first commits the durable column-authority switch after const maxBatches = boundedInteger(values['max-batches'], 100, 10000) const seconds = boundedInteger(values.seconds, 60, 3600) const pauseMs = boundedInteger(values['pause-ms'], 100, 60000) + /** URL startup parameters override driver options; pin the operator on every connection. */ + url.searchParams.set('search_path', 'public, pg_temp') const sql = postgres( - rawUrl, + url.toString(), withUtcTimestamps({ max: 1, idle_timeout: 0, @@ -434,6 +436,7 @@ The registered migration first commits the durable column-authority switch after error: describeError(error), }) } + await sleep(pauseMs) } } else { for (let position = 0; position < pending.length; ) { diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 6ed690a575a..ded9704bdfe 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -629,6 +629,67 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) + it('keeps operator discovery, assignment and verification on public under a shadow URL search path', async () => { + await database(async (sql, url) => { + const directory = await mkdtemp(join(tmpdir(), 'project-operator-shadow-')) + const manifest = join(directory, 'manifest.json') + const report = join(directory, 'report.json') + const scopedUrl = new URL(url) + scopedUrl.searchParams.set('search_path', 'shadow,public') + const run = (command: string) => + promisify(execFile)( + 'bun', + [ + '--no-env-file', + 'scripts/backfill-projects.ts', + command, + '--manifest', + manifest, + ...(command === 'plan' + ? [] + : ['--report', report, '--ack-release-drained', '--pause-ms', '1']), + ], + { + cwd: new URL('../../../apps/sim/', import.meta.url), + env: { ...process.env, MIGRATION_DATABASE_URL: scopedUrl.toString() }, + timeout: 30000, + } + ) + try { + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('public-env','Public','owner')` + await sql`CREATE SCHEMA shadow` + for (const name of [ + 'workspace', + 'project', + 'workflow', + 'user', + 'organization', + 'project_workspace', + ]) { + await sql`CREATE TABLE ${sql(`shadow.${name}`)} (LIKE ${sql(`public.${name}`)} INCLUDING ALL)` + } + await sql`INSERT INTO shadow.workspace (id,name,owner_id) VALUES ('shadow-env','Untouched','owner')` + await run('plan') + const plan = JSON.parse(await readFile(manifest, 'utf8')) + expect(plan.families.map((family: { rootId: string }) => family.rootId)).toEqual([ + 'public-env', + ]) + await run('apply') + expect(await sql`SELECT id,project_id FROM public.workspace`).toEqual([ + { id: 'public-env', project_id: expect.any(String) }, + ]) + expect(await sql`SELECT count(*)::int AS count FROM public.project`).toEqual([{ count: 1 }]) + await run('verify') + expect(await sql`SELECT id,project_id FROM shadow.workspace`).toEqual([ + { id: 'shadow-env', project_id: null }, + ]) + expect(await sql`SELECT count(*)::int AS count FROM shadow.project`).toEqual([{ count: 0 }]) + } finally { + await rm(directory, { recursive: true, force: true }) + } + }) + }) + it('runs the real operator CLI read-only, resumes bounded batches and survives a lost checkpoint', async () => { await database(async (sql, url) => { const directory = await mkdtemp(join(tmpdir(), 'project-operator-test-')) From 5cd7ada163f1828c2e67d15184a5d35b11470ca8 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 13:18:47 -0700 Subject: [PATCH 089/113] docs(projects): record native constraint rollout decisions --- packages/db/PROJECT_MEMBERSHIP.md | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 packages/db/PROJECT_MEMBERSHIP.md diff --git a/packages/db/PROJECT_MEMBERSHIP.md b/packages/db/PROJECT_MEMBERSHIP.md new file mode 100644 index 00000000000..161d4fb4492 --- /dev/null +++ b/packages/db/PROJECT_MEMBERSHIP.md @@ -0,0 +1,74 @@ +# Project membership storage and rollout + +## Current decisions + +Each workspace belongs to one Project through `workspace.project_id`; one Project can +contain many workspaces. The connector is temporary migration evidence, not the final +relationship. `project.updated_at` remains ordinary Project-record metadata, not a +workspace activity counter or a concurrency version. + +Application transactions own Project creation with its first workspace, empty-Project +prevention, archive lifecycle, workflow admission/restoration, and subtree disconnection. +Keep their Project, lineage, edge, and workspace locks. These responsibilities no longer +require permanent lifecycle/count/workflow validation triggers. + +Release 2 uses native composite foreign keys for the two structural invariants: + +- Workspace and Project organizations agree, including personal (`NULL`) scope. Both + tables have a stored generated `organization_scope_key`: `personal` for NULL and + `organization:` followed by the organization ID otherwise. The key cannot be supplied + independently by application writers. A unique Project `(id, organization_scope_key)` + index supports the workspace `(project_id, organization_scope_key)` foreign key. +- A connected fork and its parent share a Project. A unique workspace `(id, project_id)` + index supports `(forked_from_workspace_id, project_id)` referencing that pair. The + existing parent-ID foreign key retains `ON DELETE SET NULL`; it clears only the parent + pointer. The composite check uses deferred `NO ACTION`, not cascading Project moves. + +The composite constraints are `DEFERRABLE INITIALLY DEFERRED` so a transaction can move +an entire subtree or organization before validation. The ordinary required Project-ID +foreign key remains restrictive. Projects may contain disconnected roots; connected +membership does not imply one tree per Project. + +These native constraints supersede the custom Project integrity triggers, their advisory +lock wrappers, row-version dispatch, and `UPDATE project SET updated_at = updated_at`. +PostgreSQL owns referential-integrity concurrency; application business locks remain. +Drizzle records the columns, indexes, and FK relationships. The shared finalizer sets +deferred timing after fresh schema push because Drizzle does not represent that timing. + +## Two releases + +1. #8830 adds nullable membership and the authority marker. Connector-mode writes remain + connector-only. Column-mode writes use the workspace column, with connector fallback + only for unassigned legacy rows. It performs no bulk membership backfill. +2. After verifying incompatible membership-sensitive requests/workers have drained, + #8590 switches authority, performs bounded backfill/reconciliation, validates native + constraints, requires membership, and retires the connector. Its migration runs + while #8830 is serving, before the new app is promoted. + +The all-at-once traffic switch stops fresh requests to retired app servers. Remaining +in-flight operations and independently scheduled workers must be identified explicitly. +No new infrastructure maintenance mechanism, synchronization triggers, dual writes, or +extra compatibility release is required. After authority switches, #8830 is the oldest +supported application rollback; retain the column-phase marker and database changes. + +On PostgreSQL 16/17, adding stored generated scope columns rewrites their tables. The +finalizer bounds the rewrite and refuses busy tables without waiting. A timeout leaves +the connector available and requires retry/capacity assessment, never a silent increase +in lock duration. Supporting unique indexes build concurrently; constraints install as +NOT VALID and validate before contraction. This is a bounded blocking schema operation, +not an online column addition with zero write interruption. + +Backfill still verifies and repairs legacy lifecycle state before contraction. That +one-time migration prerequisite does not make lifecycle policy database-owned forever. + +## Validation and downstream work + +Use focused local Docker database checks and local HTTP servers: null scope transitions, +atomic transfers/subtree moves, parent deletion, stale snapshots/concurrent writers, +replay, schema push, application lifecycle, and the unchanged #8830 rollback runtime. +Run full CI only on GitHub Actions. Do not add Project-specific PostgreSQL 16 CI. + +Changes to these decisions apply to both #8830 and #8590. Downstream #8609 and #8610 +must retain the same storage boundary when synchronized; their separate worktrees are +not updated merely by updating this stack. Historical test reports prove earlier code, +not the current implementation or a requirement to preserve superseded trigger tests. From 36b00740a3f5134479f572aa0e36d292e7e7b8c7 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 13:20:56 -0700 Subject: [PATCH 090/113] refactor(projects): replace integrity triggers with native foreign keys --- .../__integration__/foundation.integration.ts | 25 +- .../sim/lib/projects/application/use-cases.ts | 8 +- .../db/maintenance/project-membership.sql | 201 ++++-------- .../db/migrations/meta/0407_snapshot.json | 54 ++- packages/db/schema.ts | 25 ++ .../scripts/project-contract.integration.ts | 310 ++++++++++-------- packages/db/scripts/push.integration.ts | 25 +- .../src/mocks/schema-tables.generated.ts | 12 +- 8 files changed, 362 insertions(+), 298 deletions(-) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index efec0cf451a..ad63d047cea 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -105,11 +105,25 @@ function setProjectsEnabled(enabled: boolean) { beforeEach(async () => { setProjectsEnabled(true) - const rows = await db.execute(sql`SELECT tgrelid::regclass::text AS table_name FROM pg_trigger - WHERE tgname = 'project_contract_check' - AND tgrelid IN ('project'::regclass, 'workspace'::regclass) - ORDER BY table_name`) - expect(rows).toEqual([{ table_name: 'project' }, { table_name: 'workspace' }]) + const rows = + await db.execute(sql`SELECT conname, condeferrable, condeferred, convalidated FROM pg_constraint + WHERE conrelid = 'workspace'::regclass + AND conname IN ('workspace_project_organization_fk', 'workspace_fork_project_fk') + ORDER BY conname`) + expect(rows).toEqual([ + { + conname: 'workspace_fork_project_fk', + condeferrable: true, + condeferred: true, + convalidated: true, + }, + { + conname: 'workspace_project_organization_fk', + condeferrable: true, + condeferred: true, + convalidated: true, + }, + ]) }) const request = { requestId: 'project-foundation-integration', headers: new Headers() } @@ -301,6 +315,7 @@ describe('Project foundation at the database and application boundary', () => { request, }) expect(resolved.project.id).toBe(created.project.id) + expect(resolved.project).not.toHaveProperty('organizationScopeKey') const fork = await createFork({ source, policy: await getWorkspaceCreationPolicy({ userId: f.ownerId }), diff --git a/apps/sim/lib/projects/application/use-cases.ts b/apps/sim/lib/projects/application/use-cases.ts index ece290b4d1d..70af2249d1b 100644 --- a/apps/sim/lib/projects/application/use-cases.ts +++ b/apps/sim/lib/projects/application/use-cases.ts @@ -23,7 +23,13 @@ const READ_SNAPSHOT = { isolationLevel: 'repeatable read', accessMode: 'read onl function presentProject(context: AuthorizedProject) { return { - ...context.record, + id: context.record.id, + name: context.record.name, + organizationId: context.record.organizationId, + ownerId: context.record.ownerId, + archivedAt: context.record.archivedAt, + createdAt: context.record.createdAt, + updatedAt: context.record.updatedAt, environments: context.environments, capabilities: { administer: context.canAdminister, issues: context.canUseIssues }, } diff --git a/packages/db/maintenance/project-membership.sql b/packages/db/maintenance/project-membership.sql index ee97cb6fc57..f44c99e5b82 100644 --- a/packages/db/maintenance/project-membership.sql +++ b/packages/db/maintenance/project-membership.sql @@ -90,132 +90,78 @@ DO $$ BEGIN END IF; END $$; --> statement-breakpoint -CREATE OR REPLACE FUNCTION project_contract_assert_project(target_id text) RETURNS void -LANGUAGE plpgsql SET search_path = pg_catalog, public, pg_temp AS $$ -DECLARE - record project%ROWTYPE; - total bigint; - active bigint; -BEGIN - SELECT * INTO record FROM project WHERE id = target_id; - IF NOT FOUND THEN RETURN; END IF; - SELECT count(*), count(*) FILTER (WHERE w.archived_at IS NULL) - INTO total, active FROM workspace w WHERE w.project_id = target_id; - IF total = 0 OR (record.archived_at IS NULL AND active = 0) - OR (record.archived_at IS NOT NULL AND active > 0) THEN - RAISE EXCEPTION 'Project must retain environments with consistent archive state' - USING ERRCODE = '23514', CONSTRAINT = 'project_environment_lifecycle'; +-- The generated expression always returns non-NULL, including personal scope; writers cannot override it. +-- Stored generated columns rewrite existing rows on PG16/17. Bound the rewrite and lock wait; +-- a timeout rolls this phase back and leaves the connector intact for a later retry. +BEGIN; +SET LOCAL lock_timeout = '1s'; +SET LOCAL statement_timeout = '5s'; +LOCK TABLE project, workspace IN ACCESS EXCLUSIVE MODE NOWAIT; +DO $$ BEGIN + IF to_regclass('project_workspace') IS NOT NULL THEN + LOCK TABLE project_workspace IN ACCESS EXCLUSIVE MODE NOWAIT; END IF; - IF EXISTS ( - SELECT 1 FROM workspace w - WHERE w.project_id = target_id AND w.organization_id IS DISTINCT FROM record.organization_id - ) THEN - RAISE EXCEPTION 'Project and environment organization must agree' - USING ERRCODE = '23514', CONSTRAINT = 'project_environment_organization'; +END $$; +ALTER TABLE project ADD COLUMN IF NOT EXISTS organization_scope_key text + GENERATED ALWAYS AS (CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END) STORED; +ALTER TABLE workspace ADD COLUMN IF NOT EXISTS organization_scope_key text + GENERATED ALWAYS AS (CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END) STORED; +COMMIT; +--> statement-breakpoint +-- Only invalid, unreferenced indexes from interrupted builds are removed. Valid indexes survive +-- replay, including when the final foreign keys already depend on them. +DO $$ DECLARE target_name text; BEGIN + FOR target_name IN SELECT c.relname FROM pg_index i JOIN pg_class c ON c.oid = i.indexrelid + WHERE c.relnamespace = 'public'::regnamespace AND NOT i.indisvalid + AND c.relname IN ('project_id_organization_scope_unique', 'workspace_id_project_unique') + LOOP + EXECUTE format('DROP INDEX %I', target_name); + END LOOP; +END $$; +--> statement-breakpoint +SET lock_timeout = 0; +SET statement_timeout = '60s'; +--> statement-breakpoint +CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS project_id_organization_scope_unique ON project(id, organization_scope_key); +--> statement-breakpoint +CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS workspace_id_project_unique ON workspace(id, project_id); +--> statement-breakpoint +SET lock_timeout = '1s'; +--> statement-breakpoint +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'project'::regclass + AND conname = 'project_id_organization_scope_unique') THEN + ALTER TABLE project ADD CONSTRAINT project_id_organization_scope_unique + UNIQUE USING INDEX project_id_organization_scope_unique; END IF; - IF record.archived_at IS NOT NULL AND EXISTS ( - SELECT 1 FROM workspace w JOIN workflow f ON f.workspace_id = w.id - WHERE w.project_id = target_id AND f.archived_at IS NULL - ) THEN - RAISE EXCEPTION 'Archived Project cannot contain active workflows' - USING ERRCODE = '23514', CONSTRAINT = 'project_workflow_lifecycle'; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'workspace'::regclass + AND conname = 'workspace_id_project_unique') THEN + ALTER TABLE workspace ADD CONSTRAINT workspace_id_project_unique + UNIQUE USING INDEX workspace_id_project_unique; END IF; -END; -$$; ---> statement-breakpoint -CREATE OR REPLACE FUNCTION project_contract_assert_workspace(target_id text) RETURNS void -LANGUAGE plpgsql SET search_path = pg_catalog, public, pg_temp AS $$ -DECLARE - parent_id text; - member_project_id text; - parent_project_id text; -BEGIN - SELECT forked_from_workspace_id, project_id INTO parent_id, member_project_id FROM workspace WHERE id = target_id; - IF NOT FOUND THEN RETURN; END IF; - IF parent_id IS NOT NULL THEN - SELECT project_id INTO parent_project_id FROM workspace WHERE id = parent_id; - IF parent_project_id IS DISTINCT FROM member_project_id THEN - RAISE EXCEPTION 'Connected fork environments must belong to the same Project' - USING ERRCODE = '23514', CONSTRAINT = 'workspace_fork_project'; - END IF; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'workspace'::regclass + AND conname = 'workspace_project_organization_fk') THEN + ALTER TABLE workspace ADD CONSTRAINT workspace_project_organization_fk + FOREIGN KEY (project_id, organization_scope_key) REFERENCES project(id, organization_scope_key) + DEFERRABLE INITIALLY DEFERRED NOT VALID; END IF; - IF EXISTS ( - SELECT 1 FROM workspace child - WHERE child.forked_from_workspace_id = target_id AND child.project_id IS DISTINCT FROM member_project_id - ) THEN - RAISE EXCEPTION 'Connected fork environments must belong to the same Project' - USING ERRCODE = '23514', CONSTRAINT = 'workspace_fork_project'; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'workspace'::regclass + AND conname = 'workspace_fork_project_fk') THEN + ALTER TABLE workspace ADD CONSTRAINT workspace_fork_project_fk + FOREIGN KEY (forked_from_workspace_id, project_id) REFERENCES workspace(id, project_id) + DEFERRABLE INITIALLY DEFERRED NOT VALID; END IF; -END; -$$; +END $$; --> statement-breakpoint -CREATE OR REPLACE FUNCTION project_contract_lock_projects(target_ids text[]) RETURNS void -LANGUAGE plpgsql SET search_path = pg_catalog, public, pg_temp AS $$ -DECLARE - target_id text; -BEGIN - FOR target_id IN SELECT DISTINCT id FROM unnest(target_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP - -- BEFORE row triggers already hold row locks; waiting here could invert the application lock order. - IF NOT pg_try_advisory_xact_lock(hashtextextended('project:' || target_id, 0)) THEN - RAISE EXCEPTION 'Project is changing; retry the operation' USING ERRCODE = '55P03'; - END IF; - END LOOP; -END; -$$; +-- Drizzle represents the FK columns/actions; the shared finalizer owns deferred timing on push. +ALTER TABLE workspace ALTER CONSTRAINT workspace_project_organization_fk DEFERRABLE INITIALLY DEFERRED; +ALTER TABLE workspace ALTER CONSTRAINT workspace_fork_project_fk DEFERRABLE INITIALLY DEFERRED; --> statement-breakpoint -CREATE OR REPLACE FUNCTION project_contract_before_write() RETURNS trigger -LANGUAGE plpgsql SET search_path = pg_catalog, public, pg_temp AS $$ -DECLARE - previous_id text; - next_id text; -BEGIN - IF TG_TABLE_NAME = 'project' THEN - IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; - IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; - ELSE - IF TG_OP <> 'INSERT' THEN previous_id := OLD.project_id; END IF; - IF TG_OP <> 'DELETE' THEN next_id := NEW.project_id; END IF; - END IF; - PERFORM project_contract_lock_projects(ARRAY[previous_id, next_id]); - IF TG_OP = 'DELETE' THEN RETURN OLD; END IF; - RETURN NEW; -END; -$$; +SET statement_timeout = '60s'; --> statement-breakpoint -CREATE OR REPLACE FUNCTION project_contract_after_write() RETURNS trigger -LANGUAGE plpgsql SET search_path = pg_catalog, public, pg_temp AS $$ -DECLARE - previous_id text; - next_id text; - target_ids text[]; - workspace_ids text[]; - target_id text; -BEGIN - IF TG_TABLE_NAME = 'project' THEN - -- Only the final row version needs a full scan; earlier deferred events describe superseded states. - IF TG_OP <> 'DELETE' AND EXISTS (SELECT 1 FROM project WHERE id = NEW.id AND ctid = NEW.ctid) THEN - PERFORM project_contract_assert_project(NEW.id); - END IF; - RETURN NULL; - END IF; - IF TG_OP <> 'INSERT' THEN previous_id := OLD.project_id; END IF; - IF TG_OP <> 'DELETE' THEN next_id := NEW.project_id; END IF; - target_ids := ARRAY[previous_id, next_id]; - previous_id := NULL; - next_id := NULL; - IF TG_OP <> 'INSERT' THEN previous_id := OLD.id; END IF; - IF TG_OP <> 'DELETE' THEN next_id := NEW.id; END IF; - workspace_ids := ARRAY[previous_id, next_id]; - FOR target_id IN SELECT DISTINCT id FROM unnest(target_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP - -- Touch after the mutation: immediate constraints must see it too, and repeatable-read must detect stale snapshots. - UPDATE project SET updated_at = updated_at WHERE id = target_id; - END LOOP; - FOR target_id IN SELECT DISTINCT id FROM unnest(workspace_ids) AS ids(id) WHERE id IS NOT NULL ORDER BY id LOOP - PERFORM project_contract_assert_workspace(target_id); - END LOOP; - RETURN NULL; -END; -$$; +ALTER TABLE workspace VALIDATE CONSTRAINT workspace_project_organization_fk; +--> statement-breakpoint +ALTER TABLE workspace VALIDATE CONSTRAINT workspace_fork_project_fk; --> statement-breakpoint BEGIN; --> statement-breakpoint @@ -241,29 +187,12 @@ ALTER TABLE workspace ALTER COLUMN project_id SET NOT NULL; -- migration-safe: the required column now enforces the validated helper check's invariant. ALTER TABLE workspace DROP CONSTRAINT IF EXISTS workspace_project_id_present; --> statement-breakpoint -DROP TRIGGER IF EXISTS project_contract_lock ON project; -CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OR DELETE ON project -FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); -DROP TRIGGER IF EXISTS project_contract_check ON project; -CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OR DELETE ON project -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); ---> statement-breakpoint -DROP TRIGGER IF EXISTS project_contract_lock ON workspace; -CREATE TRIGGER project_contract_lock BEFORE INSERT OR UPDATE OF id, project_id, archived_at, organization_id, forked_from_workspace_id OR DELETE ON workspace -FOR EACH ROW EXECUTE FUNCTION project_contract_before_write(); -DROP TRIGGER IF EXISTS project_contract_check ON workspace; -CREATE CONSTRAINT TRIGGER project_contract_check AFTER INSERT OR UPDATE OF id, project_id, archived_at, organization_id, forked_from_workspace_id OR DELETE ON workspace -DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION project_contract_after_write(); ---> statement-breakpoint -DROP TRIGGER IF EXISTS workspace_sync_project_membership ON workspace; -- migration-safe: contract of #8830, gated on its authority-aware release being fully deployed, incompatible app/worker versions having drained, and the column-authority switch being committed; no supported application reader or writer then needs this connector. DROP TABLE IF EXISTS project_workspace; -DROP FUNCTION IF EXISTS workspace_sync_project_membership_fn(); -DROP FUNCTION IF EXISTS project_workspace_sync_column_fn(); --> statement-breakpoint COMMIT; --> statement-breakpoint --- Membership triggers and compatible workspace-guarded writers protect new writes during validation. +-- Composite foreign keys protect structural writes; compatible application transactions own lifecycle. SELECT pg_temp.validate_project_membership(); --> statement-breakpoint DROP FUNCTION pg_temp.validate_project_membership(); diff --git a/packages/db/migrations/meta/0407_snapshot.json b/packages/db/migrations/meta/0407_snapshot.json index 3c046aa9a40..d647372ac38 100644 --- a/packages/db/migrations/meta/0407_snapshot.json +++ b/packages/db/migrations/meta/0407_snapshot.json @@ -17417,6 +17417,16 @@ "primaryKey": false, "notNull": true, "default": "now()" + }, + "organization_scope_key": { + "name": "organization_scope_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END", + "type": "stored" + } } }, "indexes": { @@ -17496,7 +17506,13 @@ } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {}, + "uniqueConstraints": { + "project_id_organization_scope_unique": { + "name": "project_id_organization_scope_unique", + "nullsNotDistinct": false, + "columns": ["id", "organization_scope_key"] + } + }, "policies": {}, "checkConstraints": { "project_name_length": { @@ -27330,6 +27346,16 @@ "primaryKey": false, "notNull": true, "default": "now()" + }, + "organization_scope_key": { + "name": "organization_scope_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END", + "type": "stored" + } } }, "indexes": { @@ -27476,10 +27502,34 @@ "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" + }, + "workspace_project_organization_fk": { + "name": "workspace_project_organization_fk", + "tableFrom": "workspace", + "tableTo": "project", + "columnsFrom": ["project_id", "organization_scope_key"], + "columnsTo": ["id", "organization_scope_key"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workspace_fork_project_fk": { + "name": "workspace_fork_project_fk", + "tableFrom": "workspace", + "tableTo": "workspace", + "columnsFrom": ["forked_from_workspace_id", "project_id"], + "columnsTo": ["id", "project_id"], + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {}, + "uniqueConstraints": { + "workspace_id_project_unique": { + "name": "workspace_id_project_unique", + "nullsNotDistinct": false, + "columns": ["id", "project_id"] + } + }, "policies": {}, "checkConstraints": { "workspace_storage_used_bytes_non_negative": { diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 5ac033fa124..72800b48a16 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2027,6 +2027,10 @@ export const workspace = pgTable( organizationId: text('organization_id').references(() => organization.id, { onDelete: 'set null', }), + /** Encodes nullable organization identity for the composite membership foreign key. */ + organizationScopeKey: text('organization_scope_key').generatedAlwaysAs( + sql`CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END` + ), projectId: text('project_id') .notNull() .references((): AnyPgColumn => project.id, { onDelete: 'restrict' }), @@ -2069,6 +2073,19 @@ export const workspace = pgTable( updatedAt: timestamp('updated_at').notNull().defaultNow(), }, (table) => ({ + idProjectUnique: unique('workspace_id_project_unique').on(table.id, table.projectId), + /** Maintenance installs these two composite checks as DEFERRABLE INITIALLY DEFERRED. */ + projectOrganizationFk: foreignKey({ + name: 'workspace_project_organization_fk', + columns: [table.projectId, table.organizationScopeKey], + foreignColumns: [project.id, project.organizationScopeKey], + }), + /** The existing parent FK clears only the parent pointer on deletion. */ + forkProjectFk: foreignKey({ + name: 'workspace_fork_project_fk', + columns: [table.forkedFromWorkspaceId, table.projectId], + foreignColumns: [table.id, table.projectId], + }), ownerIdIdx: index('workspace_owner_id_idx').on(table.ownerId), organizationIdIdx: index('workspace_organization_id_idx').on(table.organizationId), projectIdIdx: index('workspace_project_id_id_idx').on(table.projectId, table.id).concurrently(), @@ -2101,6 +2118,10 @@ export const project = pgTable( organizationId: text('organization_id').references(() => organization.id, { onDelete: 'restrict', }), + /** Encodes nullable organization identity for the composite membership foreign key. */ + organizationScopeKey: text('organization_scope_key').generatedAlwaysAs( + sql`CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END` + ), /** Lifecycle owner for personal and organization Projects; never an implicit access grant. */ ownerId: text('owner_id') .notNull() @@ -2110,6 +2131,10 @@ export const project = pgTable( updatedAt: timestamp('updated_at').notNull().defaultNow(), }, (table) => ({ + organizationScopeUnique: unique('project_id_organization_scope_unique').on( + table.id, + table.organizationScopeKey + ), nameLength: check( 'project_name_length', sql`char_length(btrim(${table.name})) BETWEEN 1 AND 100` diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index ded9704bdfe..4bd22159e79 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -131,7 +131,7 @@ async function seed(sql: Sql) { await prepareAndEnforce(sql) } -const constraintFailure = (error: unknown) => getPostgresErrorCode(error) === '23514' +const constraintFailure = (error: unknown) => getPostgresErrorCode(error) === '23503' describe('Project expand/backfill/contract against PostgreSQL', () => { it('repairs fully assigned stale Project archive and scope without restoring environments or workflows', async () => { @@ -542,8 +542,8 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sql`SELECT attnotnull FROM pg_attribute WHERE attrelid = 'public.workspace'::regclass AND attname = 'project_id'` ).toEqual([{ attnotnull: true }]) expect( - await sql`SELECT count(*)::int AS count FROM pg_trigger WHERE tgrelid IN ('public.workspace'::regclass,'public.project'::regclass) AND tgname IN ('project_contract_lock','project_contract_check')` - ).toEqual([{ count: 4 }]) + await sql`SELECT count(*)::int AS count FROM pg_trigger WHERE tgrelid IN ('public.workspace'::regclass,'public.project'::regclass) AND NOT tgisinternal` + ).toEqual([{ count: 0 }]) expect(await scoped`SELECT id,project_id FROM workspace`).toEqual([ { id: 'shadow', project_id: null }, ]) @@ -558,8 +558,8 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { ).toEqual([{ count: 0 }]) await scoped`UPDATE public.workspace SET name = 'Updated' WHERE id = 'env'` await expect( - scoped`UPDATE public.workspace SET archived_at = now() WHERE id = 'env'` - ).rejects.toMatchObject({ code: '23514' }) + scoped`UPDATE public.workspace SET organization_id = 'org' WHERE id = 'env'` + ).rejects.toMatchObject({ code: '23503' }) expect(await sql`SELECT name,archived_at FROM public.workspace`).toEqual([ { name: 'Updated', archived_at: null }, ]) @@ -1098,47 +1098,29 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) - it('validates a bulk archive once per final Project row version', async () => { + it('leaves lifecycle enforcement to the application without touching Project rows', async () => { await database(async (sql) => { - await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Root', 'owner')` - await sql`INSERT INTO workspace (id, name, owner_id, forked_from_workspace_id) - SELECT 'child-' || n, 'Child', 'owner', 'root' FROM generate_series(1, 200) n` - await prepareAndEnforce(sql) - await sql.unsafe(` - CREATE SEQUENCE project_validation_count; - ALTER FUNCTION project_contract_assert_project(text) RENAME TO measured_project_assert; - CREATE FUNCTION project_contract_assert_project(target_id text) RETURNS void LANGUAGE plpgsql AS $$ - BEGIN - PERFORM nextval('project_validation_count'); - PERFORM measured_project_assert(target_id); - END $$; - `) - await sql.begin(async (tx) => { - await tx`UPDATE workspace SET archived_at = now()` - await tx`UPDATE project SET archived_at = now()` - }) - const [calls] = - await sql`SELECT CASE WHEN is_called THEN last_value::int ELSE 0 END AS count FROM project_validation_count` - expect(calls.count).toBe(1) - const [state] = - await sql`SELECT count(*)::int AS count FROM workspace WHERE archived_at IS NOT NULL` - expect(state.count).toBe(201) + await seed(sql) + const before = await sql`SELECT id, xmin::text, updated_at FROM project` + await sql`UPDATE workspace SET archived_at = now()` + expect(await sql`SELECT id, xmin::text, updated_at FROM project`).toEqual(before) + await sql`UPDATE project SET archived_at = now()` + await sql`UPDATE workspace SET archived_at = NULL` + await sql`INSERT INTO project (id,name,owner_id) VALUES ('empty','Empty','owner')` + await sql`DELETE FROM workspace` + expect(await sql`SELECT id FROM project`).toHaveLength(2) }) }) - it('revalidates each mutation after constraints switch to immediate mode', async () => { + it('revalidates structural mutations after constraints switch to immediate mode', async () => { await database(async (sql) => { await seed(sql) await expect( sql.begin(async (tx) => { - await tx`UPDATE workspace SET archived_at = now() WHERE id = 'root'` await tx`SET CONSTRAINTS ALL IMMEDIATE` - await tx`UPDATE workspace SET archived_at = now() WHERE id = 'child'` + await tx`UPDATE workspace SET organization_id = 'org' WHERE id = 'child'` }) ).rejects.toSatisfy(constraintFailure) - const [active] = - await sql`SELECT count(*)::int AS count FROM workspace WHERE archived_at IS NULL` - expect(active.count).toBe(2) }) }) @@ -1249,7 +1231,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { { name: '0031_project_membership' }, ]) expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual(assignments) - await expect(sql`DELETE FROM workspace`).rejects.toSatisfy(constraintFailure) + await sql`DELETE FROM workspace` } finally { await runner.end() } @@ -1366,6 +1348,45 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) + it('rebuilds an interrupted unique index before attaching native constraints', async () => { + await database(async (sql) => { + await sql`INSERT INTO workspace (id,name,owner_id) VALUES ('root','Root','owner')` + await prepare(sql) + const prefix = migration.slice(0, migration.indexOf('CREATE UNIQUE INDEX CONCURRENTLY')) + await applyMigration(sql, prefix) + const held = createDeferred() + const release = createDeferred() + const reader = sql.begin(async (tx) => { + await tx`SELECT id FROM project` + held.resolve() + await release.promise + }) + await held.promise + try { + await expect( + applyMigration( + sql, + `SET statement_timeout = '100ms'; + --> statement-breakpoint + CREATE UNIQUE INDEX CONCURRENTLY project_id_organization_scope_unique + ON project(id,organization_scope_key);` + ) + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '57014') + } finally { + release.resolve() + await reader + } + expect( + await sql`SELECT indisvalid FROM pg_index WHERE indexrelid = 'project_id_organization_scope_unique'::regclass` + ).toEqual([{ indisvalid: false }]) + await applyMigration(sql, migration) + await applyMigration(sql, migration) + expect( + await sql`SELECT convalidated FROM pg_constraint WHERE conname = 'workspace_project_organization_fk'` + ).toEqual([{ convalidated: true }]) + }) + }) + it('supports a fresh database, repeated installation, and atomic first-environment creation', async () => { await database(async (sql) => { await prepareAndEnforce(sql) @@ -1375,18 +1396,15 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await tx`INSERT INTO workspace (id, project_id, name, owner_id) VALUES ('first', 'new', 'First', 'owner')` await tx`INSERT INTO workflow VALUES ('flow', 'first', NULL)` }) - await expect( - sql`INSERT INTO project (id, name, owner_id) VALUES ('empty', 'Empty', 'owner')` - ).rejects.toSatisfy(constraintFailure) - expect(await sql`SELECT 1 FROM project`).toHaveLength(1) + await sql`INSERT INTO project (id, name, owner_id) VALUES ('empty', 'Empty', 'owner')` + expect(await sql`SELECT 1 FROM project`).toHaveLength(2) }) }) - it('rejects invalid archive/scope/fork changes and commits an atomic subtree disconnect', async () => { + it('rejects invalid scope/fork changes and commits an atomic subtree disconnect', async () => { await database(async (sql) => { await seed(sql) await prepareAndEnforce(sql) - await expect(sql`UPDATE project SET archived_at = now()`).rejects.toSatisfy(constraintFailure) await expect( sql`UPDATE workspace SET organization_id = 'org' WHERE id = 'child'` ).rejects.toSatisfy(constraintFailure) @@ -1411,28 +1429,123 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) - it('requires workflows to be archived when archiving their Project and environments', async () => { + it.each([ + [null, 'org'], + ['org', null], + ['org', 'other'], + ])('rejects organization mismatch %s / %s on both sides', async (projectOrg, workspaceOrg) => { await database(async (sql) => { await seed(sql) - await prepareAndEnforce(sql) + await sql`INSERT INTO organization VALUES ('other')` + await sql.begin(async (tx) => { + await tx`UPDATE project SET organization_id = ${projectOrg}` + await tx`UPDATE workspace SET organization_id = ${projectOrg}` + }) await expect( - sql.begin(async (tx) => { - await tx`UPDATE project SET archived_at = now()` - await tx`UPDATE workspace SET archived_at = now()` - }) + sql`UPDATE workspace SET organization_id = ${workspaceOrg} WHERE id = 'child'` ).rejects.toSatisfy(constraintFailure) + await expect(sql`UPDATE project SET organization_id = ${workspaceOrg}`).rejects.toSatisfy( + constraintFailure + ) await sql.begin(async (tx) => { - await tx`UPDATE workflow SET archived_at = now()` - await tx`UPDATE workspace SET archived_at = now()` - await tx`UPDATE project SET archived_at = now()` + await tx`UPDATE workspace SET organization_id = ${workspaceOrg}` + await tx`UPDATE project SET organization_id = ${workspaceOrg}` }) + expect( + await sql`SELECT 1 FROM workspace w JOIN project p ON p.id = w.project_id + WHERE w.organization_id IS DISTINCT FROM p.organization_id` + ).toHaveLength(0) + }) + }) + + it('keeps generated scope keys distinct from organization identifiers and rejects overrides', async () => { + await database(async (sql) => { + await seed(sql) + await sql`INSERT INTO organization VALUES ('personal')` + await expect( + sql`UPDATE workspace SET organization_id = 'personal' WHERE id = 'child'` + ).rejects.toSatisfy(constraintFailure) await expect( - sql`UPDATE workspace SET archived_at = NULL WHERE id = 'root'` + sql`UPDATE workspace SET organization_scope_key = 'organization:org' WHERE id = 'child'` + ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '428C9') + }) + }) + + it('checks parent moves, allows atomic subtree moves and preserves membership on parent deletion', async () => { + await database(async (sql) => { + await seed(sql) + const [source] = await sql`SELECT project_id FROM workspace WHERE id = 'root'` + await sql`INSERT INTO project (id,name,owner_id) VALUES ('target','Target','owner')` + await sql`INSERT INTO workspace (id,project_id,name,owner_id,forked_from_workspace_id) + VALUES ('grandchild',${source.project_id},'Grandchild','owner','child')` + const before = await sql`SELECT id,xmin::text,updated_at FROM project ORDER BY id` + await expect( + sql`UPDATE workspace SET project_id = 'target' WHERE id = 'root'` ).rejects.toSatisfy(constraintFailure) - expect(await sql`SELECT 1 FROM workspace WHERE archived_at IS NULL`).toHaveLength(0) + await sql.begin(async (tx) => { + await tx`UPDATE workspace SET project_id = 'target' WHERE id = 'child'` + await tx`UPDATE workspace SET project_id = 'target' WHERE id = 'grandchild'` + await tx`UPDATE workspace SET forked_from_workspace_id = NULL WHERE id = 'child'` + }) + await sql`DELETE FROM workspace WHERE id = 'child'` + expect( + await sql`SELECT project_id,forked_from_workspace_id FROM workspace WHERE id = 'grandchild'` + ).toEqual([{ project_id: 'target', forked_from_workspace_id: null }]) + expect(await sql`SELECT id,xmin::text,updated_at FROM project ORDER BY id`).toEqual(before) + expect( + await sql`SELECT tgname FROM pg_trigger WHERE tgrelid IN ('workspace'::regclass,'project'::regclass) + AND NOT tgisinternal` + ).toHaveLength(0) }) }) + it.each(['read committed', 'repeatable read'] as const)( + 'rejects a stale fork insertion after its parent moves under %s without touching Projects', + async (isolation) => { + await database(async (sql) => { + await seed(sql) + await sql`DELETE FROM workspace WHERE id = 'child'` + await sql`INSERT INTO project (id,name,owner_id) VALUES ('target','Target','owner')` + const before = await sql`SELECT id,xmin::text FROM project ORDER BY id` + await expect( + sql.begin(`isolation level ${isolation}`, async (tx) => { + const [parent] = await tx`SELECT project_id FROM workspace WHERE id = 'root'` + await sql`UPDATE workspace SET project_id = 'target' WHERE id = 'root'` + await tx`INSERT INTO workspace (id,project_id,name,owner_id,forked_from_workspace_id) + VALUES ('stale-child',${parent.project_id},'Stale','owner','root')` + }) + ).rejects.toSatisfy((error: unknown) => + ['23503', '40001'].includes(getPostgresErrorCode(error) ?? '') + ) + expect(await sql`SELECT id FROM workspace WHERE id = 'stale-child'`).toHaveLength(0) + expect(await sql`SELECT id,xmin::text FROM project ORDER BY id`).toEqual(before) + }) + } + ) + + it.each(['read committed', 'repeatable read'] as const)( + 'rejects stale organization assignment under %s', + async (isolation) => { + await database(async (sql) => { + await seed(sql) + await expect( + sql.begin(`isolation level ${isolation}`, async (tx) => { + const [parent] = await tx`SELECT id,organization_id FROM project` + await sql.begin(async (move) => { + await move`UPDATE project SET organization_id = 'org'` + await move`UPDATE workspace SET organization_id = 'org'` + }) + await tx`INSERT INTO workspace (id,project_id,name,owner_id,organization_id) + VALUES ('stale',${parent.id},'Stale','owner',${parent.organization_id})` + }) + ).rejects.toSatisfy((error: unknown) => + ['23503', '40001'].includes(getPostgresErrorCode(error) ?? '') + ) + expect(await sql`SELECT id FROM workspace WHERE id = 'stale'`).toHaveLength(0) + }) + } + ) + it('allows concurrent workflow lifecycle writes without rewriting or locking their Project', async () => { await database(async (sql) => { await seed(sql) @@ -1464,95 +1577,6 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { }) }) - it.each([ - ['Project update', "UPDATE project SET name = 'Contending update'"], - ['Project delete', 'DELETE FROM project'], - ['environment update', "UPDATE workspace SET archived_at = now() WHERE id = 'child'"], - ['environment delete', "DELETE FROM workspace WHERE id = 'child'"], - ])('rejects a direct %s promptly when its Project is changing', async (_operation, statement) => { - await database(async (sql) => { - await seed(sql) - const held = createDeferred() - const release = createDeferred() - const writer = sql.begin(async (tx) => { - await tx`UPDATE workflow SET archived_at = now() WHERE workspace_id = 'root'` - await tx`UPDATE workspace SET archived_at = now() WHERE id = 'root'` - held.resolve() - await release.promise - }) - await held.promise - try { - await expect( - sql.begin(async (tx) => { - await tx`SET LOCAL statement_timeout = '500ms'` - await tx.unsafe(statement) - }) - ).rejects.toSatisfy((error: unknown) => getPostgresErrorCode(error) === '55P03') - } finally { - release.resolve() - await writer - } - expect( - await sql`SELECT 1 FROM workspace WHERE id = 'child' AND archived_at IS NULL` - ).toHaveLength(1) - expect(await sql`SELECT 1 FROM project WHERE name = 'Contending update'`).toHaveLength(0) - expect(Object.values(await verifyProjectBackfill(sql)).every((count) => count === 0)).toBe( - true - ) - }) - }) - - it.each(['read committed', 'repeatable read'] as const)( - 'prevents concurrent last-environment removal under %s', - async (isolation) => { - await database(async (sql) => { - await seed(sql) - await prepareAndEnforce(sql) - const archived = createDeferred() - const release = createDeferred() - const first = sql.begin(async (tx) => { - await tx`UPDATE workspace SET archived_at = now() WHERE id = 'root'` - archived.resolve() - await release.promise - }) - await archived.promise - const started = createDeferred() - const second = sql - .begin(`isolation level ${isolation}`, async (tx) => { - const [connection] = - await tx`SELECT pg_backend_pid() AS pid, count(*) FROM workspace WHERE archived_at IS NULL` - started.resolve(connection.pid) - await tx`SELECT pg_advisory_xact_lock(hashtextextended('project:' || project_id, 0)) FROM workspace WHERE id = 'child'` - await tx`UPDATE workspace SET archived_at = now() WHERE id = 'child'` - }) - .then( - () => null, - (error: unknown) => error - ) - const secondPid = await started.promise - try { - let waiting = false - for (let attempt = 0; attempt < 100; attempt++) { - const [state] = - await sql`SELECT cardinality(pg_blocking_pids(${secondPid})) > 0 AS waiting` - if (state.waiting) { - waiting = true - break - } - await sleep(10) - } - expect(waiting).toBe(true) - } finally { - release.resolve() - await first - } - const failure = await second - expect(['23514', '40001']).toContain(getPostgresErrorCode(failure)) - expect(await sql`SELECT 1 FROM workspace WHERE archived_at IS NULL`).toHaveLength(1) - }) - } - ) - it('refuses enforcement when an archived Project retains an active workflow', async () => { await database(async (sql) => { await sql`INSERT INTO workspace (id, name, owner_id) VALUES ('root', 'Root', 'owner')` @@ -1618,7 +1642,7 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { await sql`SELECT 1 FROM workspace WHERE id LIKE 'b-%' AND project_id IS NOT NULL` ).toHaveLength(0) expect( - await sql`SELECT 1 FROM pg_trigger WHERE tgname = 'project_contract_check'` + await sql`SELECT 1 FROM pg_constraint WHERE conname = 'workspace_fork_project_fk'` ).toHaveLength(0) await sql`UPDATE workspace SET organization_id = 'org' WHERE id = 'b-child'` await prepareAndEnforce(sql) diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index d8dde29680d..e122dcfe9f6 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -64,7 +64,7 @@ describe('patched Drizzle push against PostgreSQL', () => { async function schema(source: string) { await writeFile( join(directory, 'schema.ts'), - `import { pgTable, pgSchema, pgEnum, text, integer, bigint, boolean, check, timestamp } from ${JSON.stringify(import.meta.resolve('drizzle-orm/pg-core'))} + `import { pgTable, pgSchema, pgEnum, text, integer, bigint, boolean, check, timestamp, unique, foreignKey } from ${JSON.stringify(import.meta.resolve('drizzle-orm/pg-core'))} import { sql } from ${JSON.stringify(import.meta.resolve('drizzle-orm'))} ${source}` ) @@ -242,7 +242,7 @@ export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), pr 30_000 ) - it('installs Project lifecycle enforcement through the fresh-push reconciler and preserves it on replay', async () => { + it('installs native Project constraints through the fresh-push reconciler and preserves it on replay', async () => { function reconcileProjects() { const result = spawnSync( 'bun', @@ -264,13 +264,19 @@ export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), pr }, (t) => [check('project_membership_rollout_singleton', sql\`\${t.id} = 'membership'\`), check('project_membership_rollout_phase', sql\`\${t.phase} IN ('connector', 'column')\`)]) export const projects = pgTable('project', { id: text('id').primaryKey(), name: text('name').notNull(), ownerId: text('owner_id').notNull(), + organizationScopeKey: text('organization_scope_key').generatedAlwaysAs(sql\`CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END\`), organizationId: text('organization_id'), archivedAt: timestamp('archived_at'), updatedAt: timestamp('updated_at').notNull().defaultNow(), -}) +}, (t) => [unique('project_id_organization_scope_unique').on(t.id, t.organizationScopeKey)]) export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), name: text('name').notNull(), ownerId: text('owner_id').notNull(), projectId: text('project_id').notNull().references(() => projects.id, { onDelete: 'restrict' }), - organizationId: text('organization_id'), archivedAt: timestamp('archived_at'), forkedFromWorkspaceId: text('forked_from_workspace_id'), -}) + organizationScopeKey: text('organization_scope_key').generatedAlwaysAs(sql\`CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END\`), + organizationId: text('organization_id'), archivedAt: timestamp('archived_at'), forkedFromWorkspaceId: text('forked_from_workspace_id').references(() => workspaces.id, { onDelete: 'set null' }), +}, (t) => [ + unique('workspace_id_project_unique').on(t.id, t.projectId), + foreignKey({name: 'workspace_project_organization_fk', columns: [t.projectId,t.organizationScopeKey], foreignColumns: [projects.id,projects.organizationScopeKey]}), + foreignKey({name: 'workspace_fork_project_fk', columns: [t.forkedFromWorkspaceId,t.projectId], foreignColumns: [t.id,t.projectId]}), +]) export const workflows = pgTable('workflow', { id: text('id').primaryKey(), workspaceId: text('workspace_id'), archivedAt: timestamp('archived_at'), })`) @@ -281,9 +287,8 @@ export const workflows = pgTable('workflow', { expect(await sql`SELECT id, phase FROM project_membership_rollout`).toEqual([ { id: 'membership', phase: 'column' }, ]) - await expect( - sql`INSERT INTO project (id,name,owner_id) VALUES ('empty','Empty','owner')` - ).rejects.toMatchObject({ code: '23514' }) + await sql`INSERT INTO project (id,name,owner_id) VALUES ('empty','Empty','owner')` + await sql`DELETE FROM project WHERE id = 'empty'` await sql.begin(async (tx) => { await tx`INSERT INTO project (id,name,owner_id) VALUES ('family','Family','owner')` await tx`INSERT INTO workspace (id,name,owner_id,project_id,forked_from_workspace_id) VALUES ('root','Root','owner','family',NULL), ('fork','Fork','owner','family','root')` @@ -297,10 +302,10 @@ export const workflows = pgTable('workflow', { { id: 'fork', project_id: 'family' }, { id: 'root', project_id: 'family' }, ]) - await expect(sql`DELETE FROM workspace`).rejects.toMatchObject({ code: '23514' }) await expect( sql`UPDATE workspace SET organization_id = 'other' WHERE id = 'fork'` - ).rejects.toMatchObject({ code: '23514' }) + ).rejects.toMatchObject({ code: '23503' }) + await sql`DELETE FROM workspace` expect(await sql`SELECT to_regclass('public.project_workspace') AS legacy`).toEqual([ { legacy: null }, ]) diff --git a/packages/testing/src/mocks/schema-tables.generated.ts b/packages/testing/src/mocks/schema-tables.generated.ts index 5b966509fb4..227b93d1260 100644 --- a/packages/testing/src/mocks/schema-tables.generated.ts +++ b/packages/testing/src/mocks/schema-tables.generated.ts @@ -489,6 +489,7 @@ export const GENERATED_SCHEMA_TABLES = { 'logoUrl', 'ownerId', 'organizationId', + 'organizationScopeKey', 'projectId', 'workspaceMode', 'billedAccountUserId', @@ -506,7 +507,16 @@ export const GENERATED_SCHEMA_TABLES = { 'createdAt', 'updatedAt', ], - project: ['id', 'name', 'organizationId', 'ownerId', 'archivedAt', 'createdAt', 'updatedAt'], + project: [ + 'id', + 'name', + 'organizationId', + 'organizationScopeKey', + 'ownerId', + 'archivedAt', + 'createdAt', + 'updatedAt', + ], projectMembershipRollout: ['id', 'phase'], workspaceForkResourceMap: [ 'id', From 467d269d951a23cbd3cfe3c47e296454364c01b2 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 13:34:14 -0700 Subject: [PATCH 091/113] fix(projects): recognize completed rollout without its marker --- .../billing/organizations/lock-order.test.ts | 2 +- .../__integration__/foundation.integration.ts | 54 +++++++++++++++++++ apps/sim/lib/projects/environment-source.ts | 14 +++++ packages/db/PROJECT_MEMBERSHIP.md | 6 ++- packages/db/schema.ts | 2 +- packages/testing/src/mocks/database.mock.ts | 13 ++++- 6 files changed, 86 insertions(+), 5 deletions(-) diff --git a/apps/sim/lib/billing/organizations/lock-order.test.ts b/apps/sim/lib/billing/organizations/lock-order.test.ts index 3b8819eb326..9d155360293 100644 --- a/apps/sim/lib/billing/organizations/lock-order.test.ts +++ b/apps/sim/lib/billing/organizations/lock-order.test.ts @@ -235,7 +235,7 @@ describe('workspace payer-change transaction lock ordering', () => { return chain } const tx = { - execute: async () => [], + execute: async () => [{ marker: true, complete: false }], select, selectDistinct: select, insert: () => ({ diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index e1292b3f273..eed5e20ea18 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -314,6 +314,60 @@ afterAll(async () => { }) describe('Project foundation at the database and application boundary', () => { + check( + 'completed contraction needs no rollout marker, but incomplete schemas fail closed', + async () => { + const f = await fixture(false, 1) + const rollback = new Error('Restore expansion schema after contraction proof') + await expect( + db.transaction(async (tx) => { + await tx.execute(sql`LOCK TABLE workspace IN ACCESS EXCLUSIVE MODE NOWAIT`) + await tx.execute(sql`DROP TABLE project_membership_rollout`) + await expect(getProjectMembershipPhase(tx)).rejects.toThrow('authority is missing') + await tx.execute(sql`DROP TABLE project_workspace`) + await expect(getProjectMembershipPhase(tx)).rejects.toThrow('authority is missing') + await tx.execute(sql`ALTER TABLE workspace ALTER COLUMN project_id SET NOT NULL`) + await expect(getProjectMembershipPhase(tx)).rejects.toThrow('authority is missing') + await tx.execute(sql`ALTER TABLE project ADD COLUMN organization_scope_key text + GENERATED ALWAYS AS (CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END) STORED`) + await tx.execute(sql`ALTER TABLE workspace ADD COLUMN organization_scope_key text + GENERATED ALWAYS AS (CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END) STORED`) + await tx.execute( + sql`ALTER TABLE project ADD CONSTRAINT project_id_organization_scope_unique UNIQUE (id, organization_scope_key)` + ) + await tx.execute( + sql`ALTER TABLE workspace ADD CONSTRAINT workspace_id_project_unique UNIQUE (id, project_id)` + ) + await tx.execute(sql`ALTER TABLE workspace ADD CONSTRAINT workspace_project_organization_fk + FOREIGN KEY (project_id, organization_scope_key) REFERENCES project(id, organization_scope_key) DEFERRABLE INITIALLY DEFERRED`) + await tx.execute(sql`ALTER TABLE workspace ADD CONSTRAINT workspace_fork_project_fk + FOREIGN KEY (forked_from_workspace_id, project_id) REFERENCES workspace(id, project_id) DEFERRABLE INITIALLY DEFERRED`) + await tx.execute( + sql`ALTER TABLE workspace VALIDATE CONSTRAINT workspace_project_id_project_id_fk` + ) + expect(await getProjectMembershipPhase(tx)).toBe('column') + const created = await createWorkspaceInTransaction(tx, { + userId: f.ownerId, + name: 'After contraction', + organizationId: null, + observedOrganizationId: null, + governingPermissionGroupOrganizationId: null, + workspaceMode: 'personal', + billedAccountUserId: f.ownerId, + skipDefaultWorkflow: true, + }) + const environments = await getProjectEnvironmentSource(tx) + const [row] = await tx + .select({ projectId: environments.projectId }) + .from(environments) + .where(eq(environments.id, created.id)) + expect(row.projectId).toEqual(expect.any(String)) + throw rollback + }) + ).rejects.toBe(rollback) + } + ) + for (const phase of ['connector', 'column'] as const) { check(`workspace creation and fork/disconnect use only ${phase} authority`, async () => { setProjectsEnabled(false) diff --git a/apps/sim/lib/projects/environment-source.ts b/apps/sim/lib/projects/environment-source.ts index e23d5caa1d5..473179dd7e8 100644 --- a/apps/sim/lib/projects/environment-source.ts +++ b/apps/sim/lib/projects/environment-source.ts @@ -39,6 +39,20 @@ export async function getProjectMembershipPhase( tx: DbTransaction ): Promise<'connector' | 'column'> { await lockProjectMembershipBarrier(tx) + const [schema] = await tx.execute<{ marker: boolean; complete: boolean }>(sql` + SELECT to_regclass('public.project_membership_rollout') IS NOT NULL AS marker, + to_regclass('public.project_workspace') IS NULL + AND EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'public.workspace'::regclass + AND attname = 'project_id' AND attnotnull AND NOT attisdropped) + AND (SELECT count(*) = 3 FROM pg_constraint + WHERE conrelid = 'public.workspace'::regclass AND contype = 'f' AND convalidated + AND conname IN ('workspace_project_id_project_id_fk', + 'workspace_project_organization_fk', 'workspace_fork_project_fk')) AS complete + `) + if (!schema?.marker) { + if (schema?.complete) return 'column' + throw new Error('Project membership authority is missing or invalid') + } const [state] = await tx .select({ phase: projectMembershipRollout.phase }) .from(projectMembershipRollout) diff --git a/packages/db/PROJECT_MEMBERSHIP.md b/packages/db/PROJECT_MEMBERSHIP.md index 161d4fb4492..9a16ffa8292 100644 --- a/packages/db/PROJECT_MEMBERSHIP.md +++ b/packages/db/PROJECT_MEMBERSHIP.md @@ -42,14 +42,16 @@ deferred timing after fresh schema push because Drizzle does not represent that only for unassigned legacy rows. It performs no bulk membership backfill. 2. After verifying incompatible membership-sensitive requests/workers have drained, #8590 switches authority, performs bounded backfill/reconciliation, validates native - constraints, requires membership, and retires the connector. Its migration runs + constraints, requires membership, and retires the connector and authority marker atomically. Its migration runs while #8830 is serving, before the new app is promoted. The all-at-once traffic switch stops fresh requests to retired app servers. Remaining in-flight operations and independently scheduled workers must be identified explicitly. No new infrastructure maintenance mechanism, synchronization triggers, dual writes, or extra compatibility release is required. After authority switches, #8830 is the oldest -supported application rollback; retain the column-phase marker and database changes. +supported application rollback; retain the database changes. #8830 recognizes the completed schema (no connector or +marker, required membership, and validated foreign keys) as column authority; a missing +marker before completion remains an error. This supersedes retaining the marker for rollback. On PostgreSQL 16/17, adding stored generated scope columns rewrites their tables. The finalizer bounds the rewrite and refuses busy tables without waiting. A timeout leaves diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 6ab9beecbfa..1c36a1581d1 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2122,7 +2122,7 @@ export const project = pgTable( }) ) -/** Durable authority selection; retained after connector retirement for expansion rollback. */ +/** contract-pending(after #8830 is fully deployed): #8590 drops this with the connector; readers recognize the completed schema. */ export const projectMembershipRollout = pgTable( 'project_membership_rollout', { diff --git a/packages/testing/src/mocks/database.mock.ts b/packages/testing/src/mocks/database.mock.ts index 994e989d02b..c2406d06b59 100644 --- a/packages/testing/src/mocks/database.mock.ts +++ b/packages/testing/src/mocks/database.mock.ts @@ -244,7 +244,18 @@ const update = chainSpy() const set = chainSpy() const del = chainSpy() const returning = vi.fn(() => Promise.resolve([] as unknown[])) -const execute = vi.fn(() => Promise.resolve([] as unknown[])) +const execute = vi.fn((statement?: unknown) => { + if ( + statement && + typeof statement === 'object' && + 'toSQL' in statement && + typeof statement.toSQL === 'function' && + statement.toSQL().sql.includes("to_regclass('public.project_membership_rollout')") + ) { + return Promise.resolve([{ marker: true, complete: false }] as unknown[]) + } + return Promise.resolve([] as unknown[]) +}) const query = vi.fn(() => Promise.resolve([] as unknown[])) const onConflictDoUpdate = vi.fn(() => ({ returning }) as unknown as Promise) const onConflictDoNothing = vi.fn(() => ({ returning }) as unknown as Promise) From d2f089338dbc5e75b4b322ddb402376a0bcce8ac Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 13:36:50 -0700 Subject: [PATCH 092/113] fix(projects): retire rollout marker with legacy membership --- .../db/maintenance/project-membership.sql | 15 +++++-- packages/db/maintenance/project-rollout.ts | 39 +++++++------------ .../db/migrations/meta/0407_snapshot.json | 35 ----------------- packages/db/schema.ts | 18 --------- .../scripts/project-authority.integration.ts | 6 ++- .../scripts/project-contract.integration.ts | 6 +++ packages/db/scripts/push.integration.ts | 18 ++++----- .../scripts/reconcile-project-membership.ts | 6 +-- packages/testing/src/mocks/database.mock.ts | 17 +------- .../src/mocks/schema-tables.generated.ts | 1 - 10 files changed, 46 insertions(+), 115 deletions(-) diff --git a/packages/db/maintenance/project-membership.sql b/packages/db/maintenance/project-membership.sql index f44c99e5b82..793a5917307 100644 --- a/packages/db/maintenance/project-membership.sql +++ b/packages/db/maintenance/project-membership.sql @@ -5,9 +5,16 @@ SET statement_timeout = '60s'; --> statement-breakpoint DO $$ BEGIN IF to_regclass('public.project_membership_rollout') IS NULL THEN - RAISE EXCEPTION 'Project membership rollout marker is missing' USING ERRCODE = '55000'; - END IF; - IF NOT EXISTS (SELECT 1 FROM public.project_membership_rollout WHERE id = 'membership' AND phase = 'column') THEN + IF to_regclass('public.project_workspace') IS NOT NULL + OR NOT EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'public.workspace'::regclass + AND attname = 'project_id' AND attnotnull AND NOT attisdropped) + OR (SELECT count(*) FROM pg_constraint + WHERE conrelid = 'public.workspace'::regclass AND contype = 'f' AND convalidated + AND conname IN ('workspace_project_id_project_id_fk', + 'workspace_project_organization_fk', 'workspace_fork_project_fk')) <> 3 THEN + RAISE EXCEPTION 'Project membership rollout marker is missing before completed contraction' USING ERRCODE = '55000'; + END IF; + ELSIF NOT EXISTS (SELECT 1 FROM public.project_membership_rollout WHERE id = 'membership' AND phase = 'column') THEN RAISE EXCEPTION 'Project membership authority must switch before enforcement' USING ERRCODE = '55000'; END IF; IF NOT pg_try_advisory_lock(hashtextextended('sim:project-backfill-operator', 0)) THEN @@ -189,6 +196,8 @@ ALTER TABLE workspace DROP CONSTRAINT IF EXISTS workspace_project_id_present; --> statement-breakpoint -- migration-safe: contract of #8830, gated on its authority-aware release being fully deployed, incompatible app/worker versions having drained, and the column-authority switch being committed; no supported application reader or writer then needs this connector. DROP TABLE IF EXISTS project_workspace; +-- migration-safe: contract of #8830, whose deployed readers recognize the required column and validated foreign keys after both temporary tables disappear atomically under the workspace barrier. +DROP TABLE IF EXISTS project_membership_rollout; --> statement-breakpoint COMMIT; --> statement-breakpoint diff --git a/packages/db/maintenance/project-rollout.ts b/packages/db/maintenance/project-rollout.ts index 5c329447858..f76a2be7fa4 100644 --- a/packages/db/maintenance/project-rollout.ts +++ b/packages/db/maintenance/project-rollout.ts @@ -9,9 +9,19 @@ type ProjectMembershipPhase = 'connector' | 'column' export async function readProjectMembershipPhase( sql: Sql | TransactionSql ): Promise { - const [table] = - await sql`SELECT to_regclass('public.project_membership_rollout') IS NOT NULL AS present` - if (!table?.present) throw new Error('Project membership rollout marker is missing') + const [table] = await sql`SELECT + to_regclass('public.project_membership_rollout') IS NOT NULL AS present, + to_regclass('public.project_workspace') IS NULL + AND EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = to_regclass('public.workspace') + AND attname = 'project_id' AND attnotnull AND NOT attisdropped) + AND (SELECT count(*) = 3 FROM pg_constraint + WHERE conrelid = to_regclass('public.workspace') AND contype = 'f' AND convalidated + AND conname IN ('workspace_project_id_project_id_fk', + 'workspace_project_organization_fk', 'workspace_fork_project_fk')) AS complete` + if (!table?.present) { + if (table?.complete) return 'column' + throw new Error('Project membership rollout marker is missing before completed contraction') + } const rows = await sql< { id: string; phase: string }[] >`SELECT id, phase FROM public.project_membership_rollout` @@ -64,26 +74,3 @@ export async function switchProjectMembershipAuthority(sql: Sql): Promise } } } - -/** Only an empty final schema push may initialize a missing singleton directly in column mode. */ -export async function bootstrapProjectColumnAuthority(sql: Sql): Promise { - await sql.begin(async (tx) => { - await tx`SET LOCAL statement_timeout = '3s'` - await tx`LOCK TABLE public.workspace IN ACCESS EXCLUSIVE MODE NOWAIT` - const [state] = await tx`SELECT - to_regclass('public.project_workspace') IS NULL AS retired, - EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'public.workspace'::regclass - AND attname = 'project_id' AND attnotnull AND NOT attisdropped) AS required, - NOT EXISTS (SELECT 1 FROM public.workspace) AND NOT EXISTS (SELECT 1 FROM public.project) AS empty` - if (!state?.retired || !state.required) - throw new Error('Project authority bootstrap requires the fresh final schema') - const [table] = - await tx`SELECT to_regclass('public.project_membership_rollout') IS NOT NULL AS present` - if (!table?.present) - throw new Error('Project membership rollout table is missing from the final schema') - const rows = await tx`SELECT id, phase FROM public.project_membership_rollout` - if (!rows.length && state.empty) - await tx`INSERT INTO public.project_membership_rollout (id, phase) VALUES ('membership', 'column')` - await assertProjectColumnAuthority(tx) - }) -} diff --git a/packages/db/migrations/meta/0407_snapshot.json b/packages/db/migrations/meta/0407_snapshot.json index d647372ac38..4470b960990 100644 --- a/packages/db/migrations/meta/0407_snapshot.json +++ b/packages/db/migrations/meta/0407_snapshot.json @@ -30610,41 +30610,6 @@ "policies": {}, "checkConstraints": {}, "isRLSEnabled": false - }, - "public.project_membership_rollout": { - "name": "project_membership_rollout", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "phase": { - "name": "phase", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "'connector'" - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": { - "project_membership_rollout_singleton": { - "name": "project_membership_rollout_singleton", - "value": "\"project_membership_rollout\".\"id\" = 'membership'" - }, - "project_membership_rollout_phase": { - "name": "project_membership_rollout_phase", - "value": "\"project_membership_rollout\".\"phase\" IN ('connector', 'column')" - } - }, - "isRLSEnabled": false } }, "enums": { diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 56fbaabd684..9cbd5c30150 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -2148,24 +2148,6 @@ export const project = pgTable( }) ) -/** contract-pending(after #8830 is fully deployed): #8590 drops this with the connector; readers recognize the completed schema. */ -export const projectMembershipRollout = pgTable( - 'project_membership_rollout', - { - id: text('id').primaryKey(), - phase: text('phase', { enum: ['connector', 'column'] }) - .notNull() - .default('connector'), - }, - (table) => ({ - singleton: check('project_membership_rollout_singleton', sql`${table.id} = 'membership'`), - phase: check( - 'project_membership_rollout_phase', - sql`${table.phase} IN ('connector', 'column')` - ), - }) -) - export const workspaceForkResourceTypeEnum = pgEnum('workspace_fork_resource_type', [ 'workflow', 'oauth_credential', diff --git a/packages/db/scripts/project-authority.integration.ts b/packages/db/scripts/project-authority.integration.ts index ffe181cf319..433516066c8 100644 --- a/packages/db/scripts/project-authority.integration.ts +++ b/packages/db/scripts/project-authority.integration.ts @@ -1,6 +1,7 @@ import { execFile } from 'node:child_process' import { readFile } from 'node:fs/promises' import { promisify } from 'node:util' +import { readProjectMembershipPhase } from '@sim/db/maintenance/project-rollout' import { runScriptMigrations, scriptMigrations } from '@sim/db/script-migrations' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { generateId } from '@sim/utils/id' @@ -52,7 +53,7 @@ function migrate(sql: Sql) { } async function phase(sql: Sql) { - return sql`SELECT phase FROM project_membership_rollout WHERE id = 'membership'` + return [{ phase: await readProjectMembershipPhase(sql) }] } describe('Project membership authority cutover', () => { @@ -66,6 +67,9 @@ describe('Project membership authority cutover', () => { }) await migrate(runner) expect(await phase(sql)).toEqual([{ phase: 'column' }]) + expect(await sql`SELECT to_regclass('public.project_membership_rollout') AS marker`).toEqual([ + { marker: null }, + ]) expect(await sql`SELECT name,project_id FROM workspace`).toEqual([ { name: 'Legacy write', project_id: 'retained' }, ]) diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 4bd22159e79..741473e53a8 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -296,6 +296,9 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { expect(await sql`SELECT to_regclass('project_workspace')::text AS connector`).toEqual([ { connector: null }, ]) + expect(await sql`SELECT to_regclass('public.project_membership_rollout') AS marker`).toEqual([ + { marker: null }, + ]) const assignments = await sql`SELECT id,project_id FROM workspace ORDER BY id` await runRegisteredProjectMigration(url) expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual(assignments) @@ -1215,6 +1218,9 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { expect(await sql`SELECT to_regclass('project_workspace')::text AS connector`).toEqual([ { connector: null }, ]) + expect( + await sql`SELECT to_regclass('public.project_membership_rollout') AS marker` + ).toEqual([{ marker: null }]) expect(await sql`SELECT * FROM script_migrations`).toHaveLength(0) await expect( sql`INSERT INTO workspace (id,name,owner_id) VALUES ('bad','Bad','owner')` diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index e122dcfe9f6..cd67bfcb906 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -223,10 +223,7 @@ export const knowledgeBases = pgTable('knowledge_base', { await sql`ALTER TABLE workspace ADD COLUMN project_id text` await sql`UPDATE workspace SET project_id = 'retained'` } - await schema(`export const rollout = pgTable('project_membership_rollout', { - id: text('id').primaryKey(), phase: text('phase').notNull().default('connector'), -}, (t) => [check('project_membership_rollout_singleton', sql\`\${t.id} = 'membership'\`), check('project_membership_rollout_phase', sql\`\${t.phase} IN ('connector', 'column')\`)]) -export const projects = pgTable('project', { id: text('id').primaryKey() }) + await schema(`export const projects = pgTable('project', { id: text('id').primaryKey() }) export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), projectId: text('project_id').notNull() })`) const result = runPush(['--force']) expect(result.error).toBeUndefined() @@ -259,10 +256,7 @@ export const workspaces = pgTable('workspace', { id: text('id').primaryKey(), pr expect(result.error).toBeUndefined() expect(result.status, result.stdout + result.stderr).toBe(0) } - await schema(`export const rollout = pgTable('project_membership_rollout', { - id: text('id').primaryKey(), phase: text('phase').notNull().default('connector'), -}, (t) => [check('project_membership_rollout_singleton', sql\`\${t.id} = 'membership'\`), check('project_membership_rollout_phase', sql\`\${t.phase} IN ('connector', 'column')\`)]) -export const projects = pgTable('project', { + await schema(`export const projects = pgTable('project', { id: text('id').primaryKey(), name: text('name').notNull(), ownerId: text('owner_id').notNull(), organizationScopeKey: text('organization_scope_key').generatedAlwaysAs(sql\`CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END\`), organizationId: text('organization_id'), archivedAt: timestamp('archived_at'), updatedAt: timestamp('updated_at').notNull().defaultNow(), @@ -284,8 +278,8 @@ export const workflows = pgTable('workflow', { expect(first.error, first.stderr).toBeUndefined() expect(first.status, first.stdout + first.stderr).toBe(0) reconcileProjects() - expect(await sql`SELECT id, phase FROM project_membership_rollout`).toEqual([ - { id: 'membership', phase: 'column' }, + expect(await sql`SELECT to_regclass('public.project_membership_rollout') AS marker`).toEqual([ + { marker: null }, ]) await sql`INSERT INTO project (id,name,owner_id) VALUES ('empty','Empty','owner')` await sql`DELETE FROM project WHERE id = 'empty'` @@ -297,7 +291,9 @@ export const workflows = pgTable('workflow', { expect(repeated.error, repeated.stderr).toBeUndefined() expect(repeated.status, repeated.stdout + repeated.stderr).toBe(0) reconcileProjects() - expect(await sql`SELECT phase FROM project_membership_rollout`).toEqual([{ phase: 'column' }]) + expect(await sql`SELECT to_regclass('public.project_membership_rollout') AS marker`).toEqual([ + { marker: null }, + ]) expect(await sql`SELECT id,project_id FROM workspace ORDER BY id`).toEqual([ { id: 'fork', project_id: 'family' }, { id: 'root', project_id: 'family' }, diff --git a/packages/db/scripts/reconcile-project-membership.ts b/packages/db/scripts/reconcile-project-membership.ts index 10e98687f3b..38f212df5d8 100644 --- a/packages/db/scripts/reconcile-project-membership.ts +++ b/packages/db/scripts/reconcile-project-membership.ts @@ -1,5 +1,4 @@ import { enforceProjectMembership } from '@sim/db/maintenance/project-enforcement' -import { bootstrapProjectColumnAuthority } from '@sim/db/maintenance/project-rollout' import { createLogger } from '@sim/logger' import { getErrorMessage, getPostgresErrorCode } from '@sim/utils/errors' import postgres from 'postgres' @@ -24,10 +23,9 @@ try { ) } if (!process.argv.includes('--prepare') && state.workspace && state.project) { - // Drizzle cannot express lifecycle triggers; fresh push uses the same enforcement as migrations. - await bootstrapProjectColumnAuthority(sql) + /** Drizzle cannot express deferred FK timing; fresh push shares the migration finalizer. */ await enforceProjectMembership(sql) - logger.info('Project membership validation and lifecycle enforcement completed') + logger.info('Project membership validation and native constraints completed') } } catch (error) { logger.error('Project schema push stopped; resolve the error and retry', { diff --git a/packages/testing/src/mocks/database.mock.ts b/packages/testing/src/mocks/database.mock.ts index c2406d06b59..a075a48a5f4 100644 --- a/packages/testing/src/mocks/database.mock.ts +++ b/packages/testing/src/mocks/database.mock.ts @@ -151,10 +151,6 @@ function dequeueChainRows(tables: unknown[]): unknown[] | null { const queue = tableRowQueues.get(table) if (queue && queue.length > 0) return queue.shift() ?? null } - /** Default unit fixtures represent a migrated database after authority cutover. */ - if (tables.includes(schemaMock.projectMembershipRollout)) { - return [{ id: 'membership', phase: 'column' }] - } return null } @@ -244,18 +240,7 @@ const update = chainSpy() const set = chainSpy() const del = chainSpy() const returning = vi.fn(() => Promise.resolve([] as unknown[])) -const execute = vi.fn((statement?: unknown) => { - if ( - statement && - typeof statement === 'object' && - 'toSQL' in statement && - typeof statement.toSQL === 'function' && - statement.toSQL().sql.includes("to_regclass('public.project_membership_rollout')") - ) { - return Promise.resolve([{ marker: true, complete: false }] as unknown[]) - } - return Promise.resolve([] as unknown[]) -}) +const execute = vi.fn(() => Promise.resolve([] as unknown[])) const query = vi.fn(() => Promise.resolve([] as unknown[])) const onConflictDoUpdate = vi.fn(() => ({ returning }) as unknown as Promise) const onConflictDoNothing = vi.fn(() => ({ returning }) as unknown as Promise) diff --git a/packages/testing/src/mocks/schema-tables.generated.ts b/packages/testing/src/mocks/schema-tables.generated.ts index 227b93d1260..277f83992ea 100644 --- a/packages/testing/src/mocks/schema-tables.generated.ts +++ b/packages/testing/src/mocks/schema-tables.generated.ts @@ -517,7 +517,6 @@ export const GENERATED_SCHEMA_TABLES = { 'createdAt', 'updatedAt', ], - projectMembershipRollout: ['id', 'phase'], workspaceForkResourceMap: [ 'id', 'childWorkspaceId', From 5dc6f90e5a3a34aa368e2d72f6b88cc6f8cee67b Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 13:48:37 -0700 Subject: [PATCH 093/113] test(projects): model expansion authority in workspace move fixtures --- apps/sim/lib/workspaces/admin-move.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/sim/lib/workspaces/admin-move.test.ts b/apps/sim/lib/workspaces/admin-move.test.ts index a9cfebf1c8c..a379100a3ff 100644 --- a/apps/sim/lib/workspaces/admin-move.test.ts +++ b/apps/sim/lib/workspaces/admin-move.test.ts @@ -230,6 +230,9 @@ afterAll(resetDbChainMock) beforeEach(() => { resetDbChainMock() + /** Real Drizzle SQL bypasses the shared fragment mock; model the expansion schema explicitly. */ + dbChainMockFns.execute.mockResolvedValue([{ marker: true, complete: false }]) + dbChainMockFns.as.mockReturnValue(workspace) isInvitationExpired.mockReturnValue(false) /** * `vi.clearAllMocks` clears call records but keeps implementations, so a From 204e5f1fac21bebbb8f10585d14bb03414dd946a Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 13:50:24 -0700 Subject: [PATCH 094/113] fix(projects): keep repair reads compatible with expansion schema --- .../api/webhooks/agentmail/thread-scope.integration.ts | 5 +++-- apps/sim/lib/projects/membership.ts | 9 ++++++++- packages/db/maintenance/project-membership.sql | 4 ++++ packages/db/scripts/project-contract.integration.ts | 3 ++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/apps/sim/app/api/webhooks/agentmail/thread-scope.integration.ts b/apps/sim/app/api/webhooks/agentmail/thread-scope.integration.ts index c165e118950..4f7600e0f23 100644 --- a/apps/sim/app/api/webhooks/agentmail/thread-scope.integration.ts +++ b/apps/sim/app/api/webhooks/agentmail/thread-scope.integration.ts @@ -1,3 +1,4 @@ +import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures' /** * An inbox reply continues its parent task's chat, and the parent is found by the * `In-Reply-To` header — a value every recipient of the agent's reply holds. Against @@ -97,7 +98,7 @@ async function seedWorkspace(ws: InboxWorkspace): Promise { createdAt: now, updatedAt: now, }) - await db.insert(workspace).values({ + await insertWorkspaceFixture(db, { id: ws.id, name: 'Inbox thread scope fixture', ownerId: ws.ownerId, @@ -224,7 +225,7 @@ describe('inbox reply threading stays inside the receiving workspace', () => { afterAll(async () => { const ownerIds = [victim.ownerId, attacker.ownerId] await db.delete(permissions).where(inArray(permissions.userId, ownerIds)) - await db.delete(workspace).where(inArray(workspace.id, [victim.id, attacker.id])) + await deleteWorkspaceFixture(db, inArray(workspace.id, [victim.id, attacker.id])) await db.delete(user).where(inArray(user.id, ownerIds)) }) diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index e24a028ac62..c5ed9f0101a 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -143,7 +143,14 @@ export async function lockWorkspaceProject(tx: DbTransaction, workspaceId: strin throw new ProjectConflictError('Project membership needs reconciliation') await acquireAdvisoryXactLock(tx, 'project', projectLockKey(membership.projectId)) const [current] = await tx - .select({ project }) + .select({ + project: { + id: project.id, + ownerId: project.ownerId, + organizationId: project.organizationId, + archivedAt: project.archivedAt, + }, + }) .from(workspace) .innerJoin(project, eq(project.id, workspace.projectId)) .where(eq(workspace.id, workspaceId)) diff --git a/packages/db/maintenance/project-membership.sql b/packages/db/maintenance/project-membership.sql index 793a5917307..0cf2160bf16 100644 --- a/packages/db/maintenance/project-membership.sql +++ b/packages/db/maintenance/project-membership.sql @@ -1,6 +1,7 @@ -- Installed by the registered Project migration after bounded backfill and verification. COMMIT; --> statement-breakpoint +-- Validation scans and concurrent index builds have a separate 60-second budget. SET statement_timeout = '60s'; --> statement-breakpoint DO $$ BEGIN @@ -115,6 +116,8 @@ ALTER TABLE workspace ADD COLUMN IF NOT EXISTS organization_scope_key text GENERATED ALWAYS AS (CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END) STORED; COMMIT; --> statement-breakpoint +SET statement_timeout = '5s'; +--> statement-breakpoint -- Only invalid, unreferenced indexes from interrupted builds are removed. Valid indexes survive -- replay, including when the final foreign keys already depend on them. DO $$ DECLARE target_name text; BEGIN @@ -134,6 +137,7 @@ CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS project_id_organization_scope_uni CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS workspace_id_project_unique ON workspace(id, project_id); --> statement-breakpoint SET lock_timeout = '1s'; +SET statement_timeout = '5s'; --> statement-breakpoint DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'project'::regclass diff --git a/packages/db/scripts/project-contract.integration.ts b/packages/db/scripts/project-contract.integration.ts index 741473e53a8..060b99d9eb8 100644 --- a/packages/db/scripts/project-contract.integration.ts +++ b/packages/db/scripts/project-contract.integration.ts @@ -1313,7 +1313,8 @@ describe('Project expand/backfill/contract against PostgreSQL', () => { if (statement.includes('LOCK TABLE workspace,')) { const before = await locks(); await sleep(6500); - process.stdout.write(JSON.stringify({ before, after: await locks() })); + const result = JSON.stringify({ before, after: await locks() }); + await new Promise((resolve, reject) => process.stdout.write(result, (error) => error ? reject(error) : resolve())); process.exit(0); } } From 88977f39316fef393fbae7c842325a7d9fcf8bc8 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 13:57:33 -0700 Subject: [PATCH 095/113] fix(projects): retain project name for workspace archival --- .../lib/projects/__integration__/foundation.integration.ts | 7 +++++++ apps/sim/lib/projects/membership.ts | 1 + 2 files changed, 8 insertions(+) diff --git a/apps/sim/lib/projects/__integration__/foundation.integration.ts b/apps/sim/lib/projects/__integration__/foundation.integration.ts index ad63d047cea..e67dbf785bd 100644 --- a/apps/sim/lib/projects/__integration__/foundation.integration.ts +++ b/apps/sim/lib/projects/__integration__/foundation.integration.ts @@ -803,6 +803,13 @@ describe('Project foundation at the database and application boundary', () => { expect(rows.map((row) => row.archivedAt)).not.toContain(null) const [record] = await db.select().from(project).where(eq(project.id, f.projectId)) expect(record.archivedAt).not.toBeNull() + expect( + results.flatMap((result) => + result.status === 'fulfilled' && result.value.archivedProject + ? [result.value.archivedProject] + : [] + ) + ).toEqual([{ id: record.id, name: record.name }]) }) check('removing one of several environments keeps the Project active', async () => { diff --git a/apps/sim/lib/projects/membership.ts b/apps/sim/lib/projects/membership.ts index c5ed9f0101a..fe70a37461e 100644 --- a/apps/sim/lib/projects/membership.ts +++ b/apps/sim/lib/projects/membership.ts @@ -146,6 +146,7 @@ export async function lockWorkspaceProject(tx: DbTransaction, workspaceId: strin .select({ project: { id: project.id, + name: project.name, ownerId: project.ownerId, organizationId: project.organizationId, archivedAt: project.archivedAt, From 930cb76220f552bb0445b1d1cbb144e63700015e Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 14:12:26 -0700 Subject: [PATCH 096/113] docs(projects): consolidate permanent storage rules in README --- apps/sim/lib/projects/README.md | 35 +++++++++++++- packages/db/PROJECT_MEMBERSHIP.md | 76 ------------------------------- 2 files changed, 33 insertions(+), 78 deletions(-) delete mode 100644 packages/db/PROJECT_MEMBERSHIP.md diff --git a/apps/sim/lib/projects/README.md b/apps/sim/lib/projects/README.md index dc3deb8a198..df61b763da7 100644 --- a/apps/sim/lib/projects/README.md +++ b/apps/sim/lib/projects/README.md @@ -1,16 +1,47 @@ -# Project creation +# Projects A Project groups environments. An environment is an existing `workspace` record; there is no separate environment table. Every newly created Project starts with an environment. Project APIs return HTTP 503 until the `projects` feature flag is on (AppConfig on hosted deployments; the `PROJECT_API_ENABLED` secret elsewhere). The flag defaults off and gates only the Project APIs: workspace creation always assigns a Project atomically, and assigned Projects keep their lifecycle protections (fork inheritance, disconnect) either way. +## Storage and consistency + +Each workspace belongs to exactly one Project through the required `workspace.project_id` +foreign key. One Project can contain many workspaces, including disconnected roots; there +is no membership connector table. Referenced Projects cannot be deleted while their +workspaces remain. `project.updated_at` is Project-record metadata, not a workspace +activity counter or a concurrency version. + +Native composite foreign keys enforce two structural invariants: + +- Workspace and Project organizations agree, including personal (`NULL`) scope. Both + tables have a stored generated `organization_scope_key`: `personal` for NULL and + `organization:` followed by the organization ID otherwise. Writers cannot supply this + key independently. A unique Project `(id, organization_scope_key)` constraint supports + the workspace `(project_id, organization_scope_key)` foreign key. +- A connected fork and its parent share a Project. A unique workspace `(id, project_id)` + constraint supports `(forked_from_workspace_id, project_id)` referencing that pair. + The parent-ID foreign key uses `ON DELETE SET NULL`, clearing only the parent pointer. + The composite foreign key uses `NO ACTION`; Project membership never cascades automatically. + +Both composite foreign keys are `DEFERRABLE INITIALLY DEFERRED`, allowing atomic +organization transfers and subtree moves before validation. Drizzle records the columns, +indexes, and relationships; the shared schema finalizer sets deferred timing after a fresh +schema push because Drizzle does not represent that timing. + +Application transactions own creation with the first workspace, nonempty Projects, +archive lifecycle, workflow admission/restoration, and subtree disconnection. Their +Project, lineage, edge, and workspace locks protect these business operations. PostgreSQL +owns referential-integrity concurrency through the native constraints; no custom Project +integrity triggers or artificial Project row touches are required. + ## Choose the creation flow | Caller | Flow | Result | | --- | --- | --- | | New Project onboarding | `POST /api/projects` | Creates a Project and its first environment together, with independently supplied names. | | Existing workspace creation UI or caller | `POST /api/workspaces` | Creates a workspace and automatically creates its Project, preserving the existing workspace response. | -| Create another environment by forking | Existing workspace fork operation | Inherits the source workspace's Project; unassigned legacy families remain unassigned until backfilled. | +| Create another environment by forking | Existing workspace fork operation | Inherits the source workspace's Project. | Both POST endpoints are internal, session-authenticated APIs. `POST /api/projects` is not a public `/api/v2` endpoint and does not accept API-key principals. Existing workspace creation endpoints remain supported. diff --git a/packages/db/PROJECT_MEMBERSHIP.md b/packages/db/PROJECT_MEMBERSHIP.md deleted file mode 100644 index 9a16ffa8292..00000000000 --- a/packages/db/PROJECT_MEMBERSHIP.md +++ /dev/null @@ -1,76 +0,0 @@ -# Project membership storage and rollout - -## Current decisions - -Each workspace belongs to one Project through `workspace.project_id`; one Project can -contain many workspaces. The connector is temporary migration evidence, not the final -relationship. `project.updated_at` remains ordinary Project-record metadata, not a -workspace activity counter or a concurrency version. - -Application transactions own Project creation with its first workspace, empty-Project -prevention, archive lifecycle, workflow admission/restoration, and subtree disconnection. -Keep their Project, lineage, edge, and workspace locks. These responsibilities no longer -require permanent lifecycle/count/workflow validation triggers. - -Release 2 uses native composite foreign keys for the two structural invariants: - -- Workspace and Project organizations agree, including personal (`NULL`) scope. Both - tables have a stored generated `organization_scope_key`: `personal` for NULL and - `organization:` followed by the organization ID otherwise. The key cannot be supplied - independently by application writers. A unique Project `(id, organization_scope_key)` - index supports the workspace `(project_id, organization_scope_key)` foreign key. -- A connected fork and its parent share a Project. A unique workspace `(id, project_id)` - index supports `(forked_from_workspace_id, project_id)` referencing that pair. The - existing parent-ID foreign key retains `ON DELETE SET NULL`; it clears only the parent - pointer. The composite check uses deferred `NO ACTION`, not cascading Project moves. - -The composite constraints are `DEFERRABLE INITIALLY DEFERRED` so a transaction can move -an entire subtree or organization before validation. The ordinary required Project-ID -foreign key remains restrictive. Projects may contain disconnected roots; connected -membership does not imply one tree per Project. - -These native constraints supersede the custom Project integrity triggers, their advisory -lock wrappers, row-version dispatch, and `UPDATE project SET updated_at = updated_at`. -PostgreSQL owns referential-integrity concurrency; application business locks remain. -Drizzle records the columns, indexes, and FK relationships. The shared finalizer sets -deferred timing after fresh schema push because Drizzle does not represent that timing. - -## Two releases - -1. #8830 adds nullable membership and the authority marker. Connector-mode writes remain - connector-only. Column-mode writes use the workspace column, with connector fallback - only for unassigned legacy rows. It performs no bulk membership backfill. -2. After verifying incompatible membership-sensitive requests/workers have drained, - #8590 switches authority, performs bounded backfill/reconciliation, validates native - constraints, requires membership, and retires the connector and authority marker atomically. Its migration runs - while #8830 is serving, before the new app is promoted. - -The all-at-once traffic switch stops fresh requests to retired app servers. Remaining -in-flight operations and independently scheduled workers must be identified explicitly. -No new infrastructure maintenance mechanism, synchronization triggers, dual writes, or -extra compatibility release is required. After authority switches, #8830 is the oldest -supported application rollback; retain the database changes. #8830 recognizes the completed schema (no connector or -marker, required membership, and validated foreign keys) as column authority; a missing -marker before completion remains an error. This supersedes retaining the marker for rollback. - -On PostgreSQL 16/17, adding stored generated scope columns rewrites their tables. The -finalizer bounds the rewrite and refuses busy tables without waiting. A timeout leaves -the connector available and requires retry/capacity assessment, never a silent increase -in lock duration. Supporting unique indexes build concurrently; constraints install as -NOT VALID and validate before contraction. This is a bounded blocking schema operation, -not an online column addition with zero write interruption. - -Backfill still verifies and repairs legacy lifecycle state before contraction. That -one-time migration prerequisite does not make lifecycle policy database-owned forever. - -## Validation and downstream work - -Use focused local Docker database checks and local HTTP servers: null scope transitions, -atomic transfers/subtree moves, parent deletion, stale snapshots/concurrent writers, -replay, schema push, application lifecycle, and the unchanged #8830 rollback runtime. -Run full CI only on GitHub Actions. Do not add Project-specific PostgreSQL 16 CI. - -Changes to these decisions apply to both #8830 and #8590. Downstream #8609 and #8610 -must retain the same storage boundary when synchronized; their separate worktrees are -not updated merely by updating this stack. Historical test reports prove earlier code, -not the current implementation or a requirement to preserve superseded trigger tests. From 308f6703d9775dd11e71a47d2b6b66a1f0523cb4 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 14:36:40 -0700 Subject: [PATCH 097/113] fix(files): remove stale membership imports and trigger fixture --- .../[id]/members/[memberId]/route.ts | 6 +- .../v1/admin/workspaces/[id]/members/route.ts | 6 +- ...le-folder-version-ownership.integration.ts | 87 ------------------- 3 files changed, 6 insertions(+), 93 deletions(-) diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts index 4f82e40f442..3536ff44dfb 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts @@ -33,7 +33,7 @@ import { } from '@/lib/api/contracts/v1/admin' import { parseRequest } from '@/lib/api/server' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' -import { ProjectConflictError } from '@/lib/projects/membership' +import { ProjectConflictError } from '@/lib/projects/errors' import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access' import { getWorkspaceById } from '@/lib/workspaces/permissions/utils' import { WorkspaceBillingAccountRemovalError } from '@/lib/workspaces/utils' @@ -286,7 +286,7 @@ export const DELETE = withRouteHandler( } return true }) - if (!removed) return notFoundResponse('Workspace member') + if (!removed) return adminNotFoundResponse('Workspace member') logger.info(`Admin API: Removed member ${memberId} from workspace ${workspaceId}`, { userId: existingMember.userId, @@ -310,7 +310,7 @@ export const DELETE = withRouteHandler( workspaceId, }) } catch (error) { - if (error instanceof ProjectConflictError) return conflictResponse(error.message) + if (error instanceof ProjectConflictError) return adminConflictResponse(error.message) if (error instanceof WorkspaceBillingAccountRemovalError) { return adminBadRequestResponse(error.message) } diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts index e751a732d88..4b8206b4fe4 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/members/route.ts @@ -50,7 +50,7 @@ import { import { parseRequest } from '@/lib/api/server' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { syncWorkspaceEnvCredentials } from '@/lib/credentials/environment' -import { ProjectConflictError } from '@/lib/projects/membership' +import { ProjectConflictError } from '@/lib/projects/errors' import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access' import { getWorkspaceById } from '@/lib/workspaces/permissions/utils' import { WorkspaceBillingAccountRemovalError } from '@/lib/workspaces/utils' @@ -402,7 +402,7 @@ export const DELETE = withRouteHandler( } return true }) - if (!removed) return notFoundResponse('Workspace member') + if (!removed) return adminNotFoundResponse('Workspace member') logger.info(`Admin API: Removed user ${userId} from workspace ${workspaceId}`) @@ -419,7 +419,7 @@ export const DELETE = withRouteHandler( return singleResponse({ removed: true, userId, workspaceId }) } catch (error) { - if (error instanceof ProjectConflictError) return conflictResponse(error.message) + if (error instanceof ProjectConflictError) return adminConflictResponse(error.message) if (error instanceof WorkspaceBillingAccountRemovalError) { return adminBadRequestResponse(error.message) } diff --git a/packages/db/file-folder-version-ownership.integration.ts b/packages/db/file-folder-version-ownership.integration.ts index 242e6083829..df141fb4483 100644 --- a/packages/db/file-folder-version-ownership.integration.ts +++ b/packages/db/file-folder-version-ownership.integration.ts @@ -505,90 +505,3 @@ describe('file folder and version ownership in PostgreSQL', () => { ]) }) }) - -describe('file ownership composed with enforced Project membership', () => { - it('preserves Project lifecycle checks and public metadata through folder, file, and version writes', async () => { - const admin = postgres(databaseUrl, { max: 1, onnotice: () => undefined }) - const name = `file_owner_chain_test_${generateId().replaceAll('-', '')}` - const url = new URL(databaseUrl) - url.pathname = `/${name}` - await admin.unsafe(`CREATE DATABASE "${name}"`) - const chain = postgres(url.toString(), { max: 1, onnotice: () => undefined }) - try { - await chain.unsafe(` - CREATE TABLE "user" (id text PRIMARY KEY); - CREATE TABLE organization (id text PRIMARY KEY); - CREATE TABLE workspace ( - id text PRIMARY KEY, name text NOT NULL, owner_id text NOT NULL, - organization_id text, archived_at timestamp, - forked_from_workspace_id text REFERENCES workspace(id) ON DELETE SET NULL - ); - CREATE TABLE workflow ( - id text PRIMARY KEY, workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, - archived_at timestamp - ); - CREATE TABLE folder ( - id text PRIMARY KEY, name text NOT NULL, - user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, - workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE, - resource_type text NOT NULL, parent_id text REFERENCES folder(id) ON DELETE SET NULL, - deleted_at timestamp - ); - CREATE TABLE workspace_files ( - id text PRIMARY KEY, context text NOT NULL, - user_id text NOT NULL REFERENCES "user"(id) ON DELETE CASCADE, - workspace_id text REFERENCES workspace(id) ON DELETE CASCADE, - organization_id text REFERENCES organization(id) ON DELETE CASCADE, - folder_id text REFERENCES folder(id) ON DELETE SET NULL, - chat_id text, original_name text NOT NULL DEFAULT 'document.md', deleted_at timestamp - ); - CREATE TABLE workspace_file_version ( - id text PRIMARY KEY, file_id text NOT NULL REFERENCES workspace_files(id) ON DELETE CASCADE, - workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE - ); - INSERT INTO "user" VALUES ('owner'), ('creator'); - INSERT INTO workspace (id, name, owner_id) VALUES ('environment', 'Production', 'owner'); - `) - for (const source of [ - readFileSync(new URL('./migrations/0394_project_foundation.sql', import.meta.url), 'utf8'), - readFileSync( - new URL('./migrations/0403_project_membership_enforcement.sql', import.meta.url), - 'utf8' - ), - ...migrations, - ]) { - for (const statement of source.split('--> statement-breakpoint')) { - if (statement.trim()) await chain.unsafe(statement) - } - } - const [project] = await chain<{ id: string }[]>`SELECT id FROM project` - const [before] = await chain`SELECT * FROM project WHERE id = ${project.id}` - await chain`INSERT INTO folder (id, name, user_id, resource_type, project_id) - VALUES ('folder', 'Architecture', 'creator', 'file', ${project.id})` - await chain`INSERT INTO workspace_files (id, context, user_id, project_id, folder_id) - VALUES ('file', 'project', 'creator', ${project.id}, 'folder')` - await chain`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` - const [after] = await chain`SELECT * FROM project WHERE id = ${project.id}` - expect(after).toEqual(before) - await expect(chain`DELETE FROM "user" WHERE id = 'creator'`).rejects.toMatchObject({ - code: '23503', - }) - await expect(chain`DELETE FROM workspace WHERE id = 'environment'`).rejects.toMatchObject({ - code: '23514', - }) - await chain.begin(async (tx) => { - await tx`UPDATE workspace SET archived_at = now() WHERE id = 'environment'` - await tx`UPDATE project SET archived_at = now() WHERE id = ${project.id}` - }) - expect(await chain`SELECT id, workspace_id FROM workspace_file_version`).toEqual([ - { id: 'version', workspace_id: null }, - ]) - expect(await chain`SELECT id FROM folder`).toEqual([{ id: 'folder' }]) - expect(await chain`SELECT id FROM workspace_files`).toEqual([{ id: 'file' }]) - } finally { - await chain.end({ timeout: 2 }) - await admin.unsafe(`DROP DATABASE "${name}" WITH (FORCE)`) - await admin.end() - } - }) -}) From 30bc0beb774b8f5ed5d0e06e6828f85bffc24c06 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 14:43:49 -0700 Subject: [PATCH 098/113] Bound historical Project repair fixture to compatible script migrations --- .../projects/__integration__/backfill-repair.integration.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts index 4bb72a9bbfb..c554c41c835 100644 --- a/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts +++ b/apps/sim/lib/projects/__integration__/backfill-repair.integration.ts @@ -102,7 +102,9 @@ describeWithRedis('Operator archive repair against the full compatible schema', import { migrate } from 'drizzle-orm/postgres-js/migrator'; import { runScriptMigrations, scriptMigrations } from '@sim/db/script-migrations/index'; const sql = postgres(process.env.MIGRATION_DATABASE_URL,{max:1,onnotice:()=>{}}); - try { await migrate(drizzle(sql),{migrationsFolder:process.env.FIXTURE_MIGRATIONS}); await runScriptMigrations(sql, scriptMigrations.filter((item) => item.name !== '0031_project_membership')); } + const membershipIndex = scriptMigrations.findIndex((item) => item.name === '0031_project_membership'); + if (membershipIndex < 0) throw new Error('Missing membership script migration'); + try { await migrate(drizzle(sql),{migrationsFolder:process.env.FIXTURE_MIGRATIONS}); await runScriptMigrations(sql, scriptMigrations.slice(0, membershipIndex)); } finally { await sql.end(); } `, ], From e3e0f5251c5844b9bfe734e7f98e57d35021fb9e Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 14:51:49 -0700 Subject: [PATCH 099/113] Address ownership lifecycle and realtime review findings --- .github/scripts/resolve-audit-base.sh | 1 + .../handlers/file-doc-editor.integration.ts | 24 +++++++ apps/realtime/src/handlers/file-doc-store.ts | 5 +- apps/sim/lib/projects/account-deletion.ts | 22 ++++++- .../private-purge.integration.ts | 15 ++++- apps/sim/lib/projects/resource-handoff.ts | 41 +++++++----- .../creator-lifetime.integration.ts | 3 + .../workspace/workspace-file-manager.ts | 22 ++++--- .../uploads/documents/input-identity.test.ts | 20 ++++++ .../lib/uploads/documents/input-identity.ts | 19 +++--- .../lib/uploads/utils/context-prefix.test.ts | 8 ++- apps/sim/lib/uploads/utils/file-utils.ts | 1 + apps/sim/lib/users/account-deletion.ts | 6 +- .../search-workspace-file-content.test.ts | 15 +++++ .../search-workspace-file-content.ts | 7 +++ .../shared-resource-handoff.integration.ts | 63 +++++++++++++++++++ .../lib/workspaces/access/workspace-access.ts | 1 + apps/sim/lib/workspaces/resource-handoff.ts | 11 +++- .../migrations/0410_file_creator_lifetime.sql | 2 +- scripts/audit-base-workflow.test.ts | 12 ++++ 20 files changed, 252 insertions(+), 46 deletions(-) create mode 100644 apps/sim/lib/uploads/documents/input-identity.test.ts diff --git a/.github/scripts/resolve-audit-base.sh b/.github/scripts/resolve-audit-base.sh index a69e8dc2d01..153e8745498 100644 --- a/.github/scripts/resolve-audit-base.sh +++ b/.github/scripts/resolve-audit-base.sh @@ -37,5 +37,6 @@ elif [ -n "${GITHUB_BEFORE:-}" ] && git fetch --depth=1 origin "$GITHUB_BEFORE" echo "ref=$GITHUB_BEFORE" >> "$GITHUB_OUTPUT" else + git fetch --depth=2 origin "$GITHUB_SHA" echo 'ref=HEAD~1' >> "$GITHUB_OUTPUT" fi diff --git a/apps/realtime/src/handlers/file-doc-editor.integration.ts b/apps/realtime/src/handlers/file-doc-editor.integration.ts index efe7759ec92..4d7c4ee267d 100644 --- a/apps/realtime/src/handlers/file-doc-editor.integration.ts +++ b/apps/realtime/src/handlers/file-doc-editor.integration.ts @@ -122,6 +122,30 @@ describe('Project document editor attribution over real Redis', () => { } ) + check('retirement fences a seed that has not reached Redis yet', async () => { + const name = `project-file-doc:${generateId()}/${generateId()}` + const retiredId = generateId() + const replacementId = generateId() + const retired = new Y.Doc() + const replacement = new Y.Doc() + rooms.push({ name, doc: retired }) + retired.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.flag, true) + retired.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.docIdKey, retiredId) + replacement.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.flag, true) + replacement.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.docIdKey, replacementId) + try { + expect(await store.retireDocumentGeneration(name, retiredId, replacementId)).toEqual({ + status: 'applied', + docId: retiredId, + }) + expect(await store.seedIfEmpty(name, Y.encodeStateAsUpdate(retired), 1, true)).toBe(false) + expect(await store.seedIfEmpty(name, Y.encodeStateAsUpdate(replacement), 1, true)).toBe(true) + expect(await store.getDocumentGeneration(name)).toBe(replacementId) + } finally { + replacement.destroy() + } + }) + check( 'retiring a generation fences old seeds and delayed retirement preserves the replacement', async () => { diff --git a/apps/realtime/src/handlers/file-doc-store.ts b/apps/realtime/src/handlers/file-doc-store.ts index b8fe6e8f3b0..0bac89af46e 100644 --- a/apps/realtime/src/handlers/file-doc-store.ts +++ b/apps/realtime/src/handlers/file-doc-store.ts @@ -67,7 +67,7 @@ const SEED_IF_EMPTY_SCRIPT = /** Preserve a replacement identity even before its seed exists, fencing late seeds for the retired one. */ const RETIRE_DOCUMENT_SCRIPT = - "local generation = redis.call('get', KEYS[2]); if generation ~= ARGV[1] then return false end; redis.call('set', KEYS[2], ARGV[2], 'EX', ARGV[3]); redis.call('del', KEYS[1], KEYS[4], KEYS[5]); redis.call('expire', KEYS[3], ARGV[3]); redis.call('expire', KEYS[6], ARGV[3]); return generation" + "local generation = redis.call('get', KEYS[2]); if generation and generation ~= ARGV[1] then return false end; redis.call('set', KEYS[2], ARGV[2], 'EX', ARGV[3]); redis.call('del', KEYS[1], KEYS[4], KEYS[5]); redis.call('expire', KEYS[3], ARGV[3]); redis.call('expire', KEYS[6], ARGV[3]); return generation or ARGV[1]" /** Orders a durable replacement with seeds and merges, and fences publishers in the same transaction. */ const INVALIDATE_DOCUMENT_SCRIPT = @@ -821,7 +821,8 @@ export class FileDocStore { if (!retiredDocId || !replacementDocId || retiredDocId === replacementDocId) throw new Error('Document retirement requires distinct identities') if (!this.enabled) { - if ((await this.getDocumentGeneration(name)) !== retiredDocId) return { status: 'stale' } + const generation = await this.getDocumentGeneration(name) + if (generation && generation !== retiredDocId) return { status: 'stale' } const previous = this.localInvalidations.get(name) this.localInvalidations.set(name, { version: previous?.version ?? 0, diff --git a/apps/sim/lib/projects/account-deletion.ts b/apps/sim/lib/projects/account-deletion.ts index 10ce11d00a0..38fed20d394 100644 --- a/apps/sim/lib/projects/account-deletion.ts +++ b/apps/sim/lib/projects/account-deletion.ts @@ -11,6 +11,7 @@ import { ProjectConflictError } from '@/lib/projects/errors' import { purgeProjectFilesInTx } from '@/lib/projects/files/purge' import { lockProjectBackfillWrites, lockProjects } from '@/lib/projects/membership' import { + assertProjectCreatorSuccessor, findProjectSuccessor, handoffProjectCreatorReferencesTx, listSharedResourceProjectIdsForUser, @@ -115,6 +116,7 @@ export async function getProjectAccountDeletionBlockers( ) const doomed = new Set(doomedWorkspaceIds) const blockers: string[] = [] + const creatorProjects = new Set(await listSharedResourceProjectIdsForUser(db, userId)) for (const record of records) { const decision = await planProjectDeletion( db, @@ -124,7 +126,25 @@ export async function getProjectAccountDeletionBlockers( doomed, false ) - if ('blocker' in decision) blockers.push(decision.blocker) + if ('blocker' in decision) { + blockers.push(decision.blocker) + continue + } + if ('remove' in decision || !creatorProjects.has(record.id)) continue + try { + await assertProjectCreatorSuccessor( + db, + { ...record, ownerId: decision.ownerId ?? record.ownerId }, + userId, + (environments.get(record.id) ?? []) + .filter((row) => !doomed.has(row.id)) + .map((row) => row.id), + false + ) + } catch (error) { + if (!(error instanceof ProjectConflictError)) throw error + blockers.push(error.message) + } } return blockers } diff --git a/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts b/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts index b0b158e5d22..99e7d4c3c09 100644 --- a/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts +++ b/apps/sim/lib/projects/files/__integration__/private-purge.integration.ts @@ -1,5 +1,5 @@ import { mkdtempSync } from 'node:fs' -import { chmod, mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises' +import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { db } from '@sim/db' @@ -36,6 +36,7 @@ import { prepareProjectStorageMutationInTx } from '@/lib/billing/storage/trackin import { processOutboxEventById } from '@/lib/core/outbox/service' import { prepareProjectsForAccountDeletion } from '@/lib/projects/account-deletion' import { workspaceFileStorageCleanupOutboxHandlers } from '@/lib/uploads/contexts/workspace/workspace-file-storage-cleanup-outbox' +import * as storageService from '@/lib/uploads/core/storage-service' import { UPLOAD_URL_TTL_MS } from '@/lib/uploads/upload-session/provider' import { PROJECT_FILE_UPLOAD_BINDING_KEY } from '@/lib/uploads/upload-session/types' import { deleteUserAccount } from '@/lib/users/account-deletion' @@ -644,7 +645,15 @@ describe('Private Project teardown and durable object cleanup', () => { await mkdir(prefix, { recursive: true }) await writeFile(join(prefix, 'late.bin'), 'late') } - await chmod(obstructed, 0o000) + const deleteStoredFile = storageService.deleteFile + const deletion = vi.spyOn(storageService, 'deleteFile').mockImplementation((params) => { + if (params.key.startsWith(`project/${orphanIds[0]}/`)) { + return Promise.reject( + Object.assign(new Error('Fixture prefix deletion failed'), { code: 'EACCES' }) + ) + } + return deleteStoredFile(params) + }) const { projectFilePrefixCleanupOutboxHandlers } = await import( '@/lib/projects/files/prefix-cleanup' ) @@ -689,7 +698,7 @@ describe('Private Project teardown and durable object cleanup', () => { }) expect(await readFile(join(storageRoot, live.keys[0]), 'utf8')).toBe('new') } finally { - await chmod(obstructed, 0o755) + deletion.mockRestore() } expect(await readFile(join(obstructed, 'late.bin'), 'utf8')).toBe('late') await db diff --git a/apps/sim/lib/projects/resource-handoff.ts b/apps/sim/lib/projects/resource-handoff.ts index c7401dd6b2d..a279aa9aa38 100644 --- a/apps/sim/lib/projects/resource-handoff.ts +++ b/apps/sim/lib/projects/resource-handoff.ts @@ -86,25 +86,23 @@ export async function listSharedResourceProjectIdsForUser(executor: DbOrTx, user } /** Validates the canonical Project owner rather than borrowing an environment's payer. */ -export async function handoffProjectCreatorReferencesTx( - tx: DbTransaction, +export async function assertProjectCreatorSuccessor( + executor: DbOrTx, record: typeof project.$inferSelect, departingUserId: string, - environmentIds: string[] + environmentIds: string[], + hold: boolean ) { const successorId = record.ownerId if (successorId === departingUserId) throw new ProjectConflictError( 'Project resources need a successor before this account can leave' ) - const [successor] = await tx - .select({ id: user.id }) - .from(user) - .where(eq(user.id, successorId)) - .for('key share') + const successorQuery = executor.select({ id: user.id }).from(user).where(eq(user.id, successorId)) + const [successor] = await (hold ? successorQuery.for('key share') : successorQuery) if (!successor) throw new ProjectConflictError('Project successor changed; retry the operation') - const [admin] = record.organizationId - ? await tx + const adminQuery = record.organizationId + ? executor .select({ id: member.id }) .from(member) .where( @@ -114,11 +112,11 @@ export async function handoffProjectCreatorReferencesTx( inArray(member.role, ORG_ADMIN_ROLES) ) ) - .for('share') - : [] + : null + const [admin] = adminQuery ? await (hold ? adminQuery.for('share') : adminQuery) : [] if (!admin) { - const grants = environmentIds.length - ? await tx + const grantQuery = environmentIds.length + ? executor .select({ id: permissions.entityId }) .from(permissions) .where( @@ -129,13 +127,24 @@ export async function handoffProjectCreatorReferencesTx( inArray(permissions.entityId, environmentIds) ) ) - .for('share') - : [] + : null + const grants = grantQuery ? await (hold ? grantQuery.for('share') : grantQuery) : [] if (!environmentIds.length || grants.length !== environmentIds.length) throw new ProjectConflictError( 'Project successor must administer every surviving environment' ) } +} + +/** Transfers creator references only after the canonical successor is held and validated. */ +export async function handoffProjectCreatorReferencesTx( + tx: DbTransaction, + record: typeof project.$inferSelect, + departingUserId: string, + environmentIds: string[] +) { + await assertProjectCreatorSuccessor(tx, record, departingUserId, environmentIds, true) + const successorId = record.ownerId await handoffFileCreatorsInTx( tx, and(eq(workspaceFiles.projectId, record.id), eq(workspaceFiles.userId, departingUserId)), diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts index 9d3c43104b9..8ded5b7ec10 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/creator-lifetime.integration.ts @@ -165,6 +165,9 @@ it('partial departure retains Project attribution; successor handoff and prior-d context: 'project', folderId: projectFolderId, }) + await expect(db.delete(user).where(eq(user.id, creatorId))).rejects.toMatchObject({ + cause: { code: '23514', constraint_name: 'project_resource_creator_handoff' }, + }) await db.transaction((tx) => changeProjectStoragePayersInTx(tx, [ { 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 3e3e470bbdc..680bd557831 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -45,6 +45,7 @@ import { incrementStorageUsageForBillingContextInTx, lockWorkspaceStorageForMutationInTx, maybeNotifyStorageLimitForBillingContext, + prepareFileAccountingInTx, resolveStorageBillingContext, } from '@/lib/billing/storage' import { @@ -782,7 +783,6 @@ export async function createOwnedWorkspaceFile(params: { 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, @@ -800,7 +800,11 @@ export async function createOwnedWorkspaceFile(params: { persistMetadata: false, }) try { - const { owner, updatedUsage } = await db.transaction(async (tx) => { + const { owner, updatedUsage, billing } = await db.transaction(async (tx) => { + const accounting = await prepareFileAccountingInTx(tx, { + entityType: 'workspace', + entityId: params.workspaceId, + }) const inserted = await insertWorkspaceFileMetadataInTx(tx, { id: fileId, key: uploadResult.key, @@ -813,14 +817,14 @@ export async function createOwnedWorkspaceFile(params: { 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 } + const updatedUsage = await accounting.mutation.applyDelta(buffer.length) + return { + owner: await params.insertOwner(tx, fileId), + updatedUsage, + billing: accounting.billing, + } }) - void maybeNotifyStorageLimitForBillingContext(storageBillingContext, updatedUsage) + void maybeNotifyStorageLimitForBillingContext(billing, updatedUsage) return { fileId, owner } } catch (error) { await cleanupWorkspaceStorageObject( diff --git a/apps/sim/lib/uploads/documents/input-identity.test.ts b/apps/sim/lib/uploads/documents/input-identity.test.ts new file mode 100644 index 00000000000..e14a1818f99 --- /dev/null +++ b/apps/sim/lib/uploads/documents/input-identity.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from 'vitest' +import { fileDocumentInputIdentity } from '@/lib/uploads/documents/input-identity' + +describe('compiled document input identity', () => { + it('is independent of dependency query order and still changes with a revision', () => { + const owner = { entityType: 'project', entityId: 'project-1' } as const + const files = ['b', 'a'].map((id) => ({ + id, + key: `project/project-1/${id}`, + contentUpdatedAt: new Date(1), + sizeBytes: 1, + })) + const identity = fileDocumentInputIdentity(owner, files) + expect(fileDocumentInputIdentity(owner, files.toReversed())).toBe(identity) + expect( + fileDocumentInputIdentity(owner, [{ ...files[0], contentUpdatedAt: new Date(2) }, files[1]]) + ).not.toBe(identity) + expect(files.map((file) => file.id)).toEqual(['b', 'a']) + }) +}) diff --git a/apps/sim/lib/uploads/documents/input-identity.ts b/apps/sim/lib/uploads/documents/input-identity.ts index 4074d06d5f8..c06debf7e75 100644 --- a/apps/sim/lib/uploads/documents/input-identity.ts +++ b/apps/sim/lib/uploads/documents/input-identity.ts @@ -1,21 +1,24 @@ import type { WorkspaceFileRow } from '@sim/db/schema' +import { compareStrings } from '@sim/utils/string' import type { EditableFileOwner } from '@/lib/workspace-files/ownership' /** Binds a rendered cache entry to every authorized input revision, in canonical file order. */ export function fileDocumentInputIdentity( owner: EditableFileOwner, - files: readonly WorkspaceFileRow[] + files: readonly Pick[] ) { return files.length > 0 ? JSON.stringify({ version: 1, - inputs: files.map((file) => ({ - fileId: file.id, - key: file.key, - context: owner.entityType, - contentVersion: file.contentUpdatedAt.toISOString(), - size: file.sizeBytes, - })), + inputs: files + .toSorted((left, right) => compareStrings(left.id, right.id)) + .map((file) => ({ + fileId: file.id, + key: file.key, + context: owner.entityType, + contentVersion: file.contentUpdatedAt.toISOString(), + size: file.sizeBytes, + })), }) : undefined } diff --git a/apps/sim/lib/uploads/utils/context-prefix.test.ts b/apps/sim/lib/uploads/utils/context-prefix.test.ts index c8cc8a10747..9cc82f088be 100644 --- a/apps/sim/lib/uploads/utils/context-prefix.test.ts +++ b/apps/sim/lib/uploads/utils/context-prefix.test.ts @@ -3,7 +3,13 @@ import { inferContextFromKey, tryInferContextFromKey } from '@/lib/uploads/utils describe('tryInferContextFromKey', () => { it('classifies a known prefix the same way the throwing form does', () => { - for (const key of ['workspace/a/b.txt', 'execution/a/b/c/d.bin', 'kb/x', 'logs/y']) { + for (const key of [ + 'project/a/b.txt', + 'workspace/a/b.txt', + 'execution/a/b/c/d.bin', + 'kb/x', + 'logs/y', + ]) { expect(tryInferContextFromKey(key)).toBe(inferContextFromKey(key)) } }) diff --git a/apps/sim/lib/uploads/utils/file-utils.ts b/apps/sim/lib/uploads/utils/file-utils.ts index 3582fb2079b..00e8ad82e8c 100644 --- a/apps/sim/lib/uploads/utils/file-utils.ts +++ b/apps/sim/lib/uploads/utils/file-utils.ts @@ -760,6 +760,7 @@ export function tryInferContextFromKey(key: string): StorageContext | null { if (key.startsWith('copilot/')) return 'copilot' if (key.startsWith('execution/')) return 'execution' if (key.startsWith('workspace/')) return 'workspace' + if (key.startsWith('project/')) return 'project' if (key.startsWith('assistant/')) return 'mothership' if (key.startsWith('profile-pictures/')) return 'profile-pictures' if (key.startsWith('og-images/')) return 'og-images' diff --git a/apps/sim/lib/users/account-deletion.ts b/apps/sim/lib/users/account-deletion.ts index 6db99517b7d..adee06c5563 100644 --- a/apps/sim/lib/users/account-deletion.ts +++ b/apps/sim/lib/users/account-deletion.ts @@ -895,8 +895,10 @@ export async function deleteUserAccount(userId: string): Promise { expect(mocks.folders).not.toHaveBeenCalled() expect(mocks.search).not.toHaveBeenCalled() }) + it('maps a delivery-lock timeout to a retryable orchestration error', async () => { + dbChainMockFns.transaction.mockRejectedValueOnce( + new Error('query failed', { + cause: Object.assign(new Error('lock timeout'), { code: '55P03' }), + }) + ) + await expect( + searchWorkspaceFileContent.execute({ + principal, + input, + request: { headers: new Headers() }, + }) + ).rejects.toMatchObject({ code: 'locked' }) + }) }) diff --git a/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts index c8fcb2b1c69..e01ddec8749 100644 --- a/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { workspace } from '@sim/db/schema' +import { getPostgresErrorCode } from '@sim/utils/errors' import { and, eq, isNull } from 'drizzle-orm' import { OrchestrationError } from '@/lib/core/orchestration/types' import { acquirePermissionGroupOrgLock } from '@/lib/permission-groups/locks' @@ -121,6 +122,12 @@ export const searchWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ throw new OrchestrationError('validation', error.message) } /** Nothing is wrong with the query, so the caller is told to retry, not to rewrite it. */ + if (['55P03', '25P04'].includes(getPostgresErrorCode(error) ?? '')) { + throw new OrchestrationError( + 'locked', + 'File search is briefly unavailable. Try again shortly.' + ) + } if (error instanceof WorkspaceFileSearchUnavailableError) { throw new OrchestrationError('locked', error.message) } diff --git a/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts b/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts index d55e8374f28..a62886379b0 100644 --- a/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts +++ b/apps/sim/lib/workspaces/access/shared-resource-handoff.integration.ts @@ -281,6 +281,69 @@ describe('shared resource retention across workspace departure and account erasu } ) + it('transfers a departing owner to the billed account without a preexisting grant', async () => { + const fixture = await seedResources(false) + await db + .update(workspace) + .set({ ownerId: fixture.departingId }) + .where(eq(workspace.id, fixture.workspaceId)) + await db + .delete(permissions) + .where( + and(eq(permissions.entityId, fixture.workspaceId), eq(permissions.userId, fixture.ownerId)) + ) + const result = await db.transaction((tx) => + revokeWorkspaceAccessTx(tx, { + workspaceId: fixture.workspaceId, + userId: fixture.departingId, + }) + ) + expect(result).toEqual({ revoked: true, ownershipTransferred: true }) + await assertTransferred(fixture) + expect( + await db + .select() + .from(permissions) + .where( + and( + eq(permissions.entityId, fixture.workspaceId), + eq(permissions.userId, fixture.ownerId) + ) + ) + ).toMatchObject([{ permissionType: 'admin' }]) + }) + + it('previews a missing creator-only Project successor grant before deleting an account', async () => { + const fixture = await seedResources(false) + const [binding] = await db.select().from(workspace).where(eq(workspace.id, fixture.workspaceId)) + const fileId = generateId() + projectFileIds.push(fileId) + await db.insert(workspaceFiles).values({ + id: fileId, + userId: fixture.departingId, + projectId: binding.projectId, + context: 'project', + key: `project/${binding.projectId}/${fileId}`, + originalName: 'retained', + contentType: 'text/plain', + sizeBytes: 1, + }) + await db + .update(permissions) + .set({ permissionType: 'read' }) + .where( + and(eq(permissions.entityId, fixture.workspaceId), eq(permissions.userId, fixture.ownerId)) + ) + const plan = await getAccountDeletionPlan(fixture.departingId) + expect(plan.blockers).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + message: 'Project successor must administer every surviving environment', + }), + ]) + ) + }) + it('retains workspace-logo bindings after uploader departure and deletion', async () => { const fixture = await seedResources(false) const logoId = generateId() diff --git a/apps/sim/lib/workspaces/access/workspace-access.ts b/apps/sim/lib/workspaces/access/workspace-access.ts index e9a2ed41053..f1cc86de25c 100644 --- a/apps/sim/lib/workspaces/access/workspace-access.ts +++ b/apps/sim/lib/workspaces/access/workspace-access.ts @@ -157,6 +157,7 @@ export async function revokeWorkspaceAccessTx( tx, workspaceIds: [params.workspaceId], departingUserId: params.userId, + transferOwnership: true, }) if (reassignment.unresolved.length > 0) { return { diff --git a/apps/sim/lib/workspaces/resource-handoff.ts b/apps/sim/lib/workspaces/resource-handoff.ts index c5854f7f941..5460f294270 100644 --- a/apps/sim/lib/workspaces/resource-handoff.ts +++ b/apps/sim/lib/workspaces/resource-handoff.ts @@ -48,8 +48,9 @@ export async function listSharedResourceWorkspaceIdsForUser(executor: DbOrTx, us /** Holds a real successor and their current grant through the handoff transaction. */ async function holdWorkspaceSuccessor( tx: DbTransaction, - row: { id: string; organizationId: string | null; billedAccountUserId: string }, - departingUserId: string + row: { id: string; ownerId: string; organizationId: string | null; billedAccountUserId: string }, + departingUserId: string, + transferOwnership: boolean ): Promise { const successor = row.billedAccountUserId if (!successor || successor === departingUserId) return false @@ -59,6 +60,7 @@ async function holdWorkspaceSuccessor( .where(eq(user.id, successor)) .for('key share') if (!account) return false + if (transferOwnership && row.ownerId === departingUserId) return true const [grant] = await tx .select({ id: permissions.id }) .from(permissions) @@ -95,10 +97,13 @@ export async function reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx tx, workspaceIds, departingUserId, + transferOwnership = false, }: { tx: DbTransaction workspaceIds: string[] departingUserId: string + /** The caller will grant the billed successor admin access through workspace ownership transfer. */ + transferOwnership?: boolean }): Promise<{ unresolved: string[] }> { const ids = [...new Set(workspaceIds)].sort() if (!ids.length) return { unresolved: [] } @@ -120,7 +125,7 @@ export async function reassignSharedResourceOwnershipForWorkspaceMemberRemovalTx for (const row of rows) { if ( (owned.has(row.id) || row.ownerId === departingUserId) && - !(await holdWorkspaceSuccessor(tx, row, departingUserId)) + !(await holdWorkspaceSuccessor(tx, row, departingUserId, transferOwnership)) ) unresolved.push(row.id) } diff --git a/packages/db/migrations/0410_file_creator_lifetime.sql b/packages/db/migrations/0410_file_creator_lifetime.sql index dcb5c4e841a..5ded57ae704 100644 --- a/packages/db/migrations/0410_file_creator_lifetime.sql +++ b/packages/db/migrations/0410_file_creator_lifetime.sql @@ -3,7 +3,7 @@ RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF EXISTS (SELECT 1 FROM workspace_files WHERE user_id = OLD.id AND project_id IS NOT NULL) OR EXISTS (SELECT 1 FROM folder WHERE user_id = OLD.id AND project_id IS NOT NULL) THEN - RAISE EXCEPTION 'Project resource creators must be handed to a successor before account deletion' USING ERRCODE = '23514'; + RAISE EXCEPTION 'Project resource creators must be handed to a successor before account deletion' USING ERRCODE = '23514', CONSTRAINT = 'project_resource_creator_handoff'; END IF; RETURN OLD; END; diff --git a/scripts/audit-base-workflow.test.ts b/scripts/audit-base-workflow.test.ts index 07cae7f1fe6..0f26b15787e 100644 --- a/scripts/audit-base-workflow.test.ts +++ b/scripts/audit-base-workflow.test.ts @@ -153,8 +153,20 @@ describe('diff audit base selection', () => { it('keeps the first-parent fallback for a new branch push', () => { const data = fixture() + const shallow = join(data.root, 'shallow') + git( + data.root, + 'clone', + '--quiet', + '--depth=1', + '--branch=foundation', + `file://${join(data.root, 'origin.git')}`, + shallow + ) + data.repo = shallow const result = resolveBase(data, { GITHUB_EVENT_NAME: 'push', GITHUB_BEFORE: '0'.repeat(40) }) expect(result.status, result.stderr).toBe(0) expect(result.output).toBe('ref=HEAD~1') + expect(git(data.repo, 'rev-parse', 'HEAD~1')).toBe(data.before) }) }) From 9569da888549628967c5eaa32c06208424f63bb8 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 14:54:32 -0700 Subject: [PATCH 100/113] Use compatible array copies for document identity sorting --- apps/sim/lib/uploads/documents/input-identity.test.ts | 2 +- apps/sim/lib/uploads/documents/input-identity.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sim/lib/uploads/documents/input-identity.test.ts b/apps/sim/lib/uploads/documents/input-identity.test.ts index e14a1818f99..bb9903bc697 100644 --- a/apps/sim/lib/uploads/documents/input-identity.test.ts +++ b/apps/sim/lib/uploads/documents/input-identity.test.ts @@ -11,7 +11,7 @@ describe('compiled document input identity', () => { sizeBytes: 1, })) const identity = fileDocumentInputIdentity(owner, files) - expect(fileDocumentInputIdentity(owner, files.toReversed())).toBe(identity) + expect(fileDocumentInputIdentity(owner, [...files].reverse())).toBe(identity) expect( fileDocumentInputIdentity(owner, [{ ...files[0], contentUpdatedAt: new Date(2) }, files[1]]) ).not.toBe(identity) diff --git a/apps/sim/lib/uploads/documents/input-identity.ts b/apps/sim/lib/uploads/documents/input-identity.ts index c06debf7e75..7092e4d5759 100644 --- a/apps/sim/lib/uploads/documents/input-identity.ts +++ b/apps/sim/lib/uploads/documents/input-identity.ts @@ -10,8 +10,8 @@ export function fileDocumentInputIdentity( return files.length > 0 ? JSON.stringify({ version: 1, - inputs: files - .toSorted((left, right) => compareStrings(left.id, right.id)) + inputs: [...files] + .sort((left, right) => compareStrings(left.id, right.id)) .map((file) => ({ fileId: file.id, key: file.key, From d43cdd46eec6e3ce638481965b27cee5ac2ea2cc Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sat, 10 Oct 2026 14:58:32 -0700 Subject: [PATCH 101/113] Keep foundation owned-file creation on its available billing API --- .../workspace/workspace-file-manager.ts | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) 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 680bd557831..3e3e470bbdc 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -45,7 +45,6 @@ import { incrementStorageUsageForBillingContextInTx, lockWorkspaceStorageForMutationInTx, maybeNotifyStorageLimitForBillingContext, - prepareFileAccountingInTx, resolveStorageBillingContext, } from '@/lib/billing/storage' import { @@ -783,6 +782,7 @@ export async function createOwnedWorkspaceFile(params: { 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, @@ -800,11 +800,7 @@ export async function createOwnedWorkspaceFile(params: { persistMetadata: false, }) try { - const { owner, updatedUsage, billing } = await db.transaction(async (tx) => { - const accounting = await prepareFileAccountingInTx(tx, { - entityType: 'workspace', - entityId: params.workspaceId, - }) + const { owner, updatedUsage } = await db.transaction(async (tx) => { const inserted = await insertWorkspaceFileMetadataInTx(tx, { id: fileId, key: uploadResult.key, @@ -817,14 +813,14 @@ export async function createOwnedWorkspaceFile(params: { context: params.context, }) if (!inserted) throw new Error(`Owned ${params.context} file ${fileId} was not inserted`) - const updatedUsage = await accounting.mutation.applyDelta(buffer.length) - return { - owner: await params.insertOwner(tx, fileId), - updatedUsage, - billing: accounting.billing, - } + const updatedUsage = await incrementStorageUsageForBillingContextInTx( + tx, + storageBillingContext, + buffer.length + ) + return { owner: await params.insertOwner(tx, fileId), updatedUsage } }) - void maybeNotifyStorageLimitForBillingContext(billing, updatedUsage) + void maybeNotifyStorageLimitForBillingContext(storageBillingContext, updatedUsage) return { fileId, owner } } catch (error) { await cleanupWorkspaceStorageObject( From ed0f171530afc9731049bc19c6a2c52ec81d6082 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sun, 11 Oct 2026 16:03:50 -0700 Subject: [PATCH 102/113] refactor: name the projects flag fallback PROJECTS --- apps/sim/lib/core/config/env.ts | 2 +- apps/sim/lib/core/config/feature-flags.ts | 4 ++-- apps/sim/lib/projects/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/sim/lib/core/config/env.ts b/apps/sim/lib/core/config/env.ts index cf0366b0055..ca096684b76 100644 --- a/apps/sim/lib/core/config/env.ts +++ b/apps/sim/lib/core/config/env.ts @@ -588,7 +588,7 @@ export const env = createEnv({ AGENTMAIL_DOMAIN: z.string().optional(), // Custom domain for AgentMail inboxes (default: agentmail.to) MSHIP_PLAN_MODE: z.boolean().optional(), DASHBOARDS: z.boolean().optional(), - PROJECT_API_ENABLED: z.boolean().optional(), // Fallback for the `projects` feature flag off AppConfig + PROJECTS: 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 CHANGELOG: z.boolean().optional(), // Fallback for the `changelog` feature flag off AppConfig MSHIP_MODEL_SELECTOR: z.boolean().optional(), diff --git a/apps/sim/lib/core/config/feature-flags.ts b/apps/sim/lib/core/config/feature-flags.ts index 782d947364e..ee29d67662c 100644 --- a/apps/sim/lib/core/config/feature-flags.ts +++ b/apps/sim/lib/core/config/feature-flags.ts @@ -101,8 +101,8 @@ const FEATURE_FLAGS = { description: 'Expose the Project APIs once the membership backfill has validated. Global on/off only; ' + 'workspace creation assigns Projects and lifecycle protections apply either way. ' + - 'Off-AppConfig falls back to PROJECT_API_ENABLED.', - fallback: 'PROJECT_API_ENABLED', + 'Off-AppConfig falls back to PROJECTS.', + fallback: 'PROJECTS', }, 'knowledge-member-access': { description: diff --git a/apps/sim/lib/projects/README.md b/apps/sim/lib/projects/README.md index df61b763da7..e8ad33a6b7e 100644 --- a/apps/sim/lib/projects/README.md +++ b/apps/sim/lib/projects/README.md @@ -2,7 +2,7 @@ A Project groups environments. An environment is an existing `workspace` record; there is no separate environment table. Every newly created Project starts with an environment. -Project APIs return HTTP 503 until the `projects` feature flag is on (AppConfig on hosted deployments; the `PROJECT_API_ENABLED` secret elsewhere). The flag defaults off and gates only the Project APIs: workspace creation always assigns a Project atomically, and assigned Projects keep their lifecycle protections (fork inheritance, disconnect) either way. +Project APIs return HTTP 503 until the `projects` feature flag is on (AppConfig on hosted deployments; the `PROJECTS` secret elsewhere). The flag defaults off and gates only the Project APIs: workspace creation always assigns a Project atomically, and assigned Projects keep their lifecycle protections (fork inheritance, disconnect) either way. ## Storage and consistency From 8020833c9885e538c0f1dd13c9df8424daacd991 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Sun, 11 Oct 2026 16:23:23 -0700 Subject: [PATCH 103/113] test: cover ordinary PR and squash push audit bases --- scripts/audit-base-workflow.test.ts | 70 +++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/scripts/audit-base-workflow.test.ts b/scripts/audit-base-workflow.test.ts index 0f26b15787e..f3d0fabad5f 100644 --- a/scripts/audit-base-workflow.test.ts +++ b/scripts/audit-base-workflow.test.ts @@ -48,6 +48,28 @@ function fixture() { return { root, repo, base, before, head, pr } } +function ordinaryFixture(baseBranch: string, commits: number) { + const data = fixture() + git(data.repo, 'checkout', '--quiet', '-b', baseBranch, data.base) + git(data.repo, 'checkout', '--quiet', '-b', 'feature') + for (let index = 1; index <= commits; index++) { + writeFileSync(join(data.repo, `change-${index}.ts`), `export const value = ${index}\n`) + git(data.repo, 'add', '.') + git(data.repo, 'commit', '--quiet', '-m', `change ${index}`) + } + const head = git(data.repo, 'rev-parse', 'HEAD') + git(data.repo, 'push', '--quiet', 'origin', baseBranch, 'feature') + return { + ...data, + head, + pr: { + ...data.pr, + head: { ...data.pr.head, ref: 'feature', sha: head }, + base: { ref: baseBranch, sha: data.base }, + }, + } +} + function resolveBase(data: ReturnType, overrides: Record = {}) { const output = join(data.root, 'output') writeFileSync(output, '') @@ -87,6 +109,54 @@ afterEach(() => { }) describe('diff audit base selection', () => { + it.each(['main', 'staging', 'dev'])( + 'audits every commit of an ordinary PR into %s for both PR and manual events', + (baseBranch) => { + const data = ordinaryFixture(baseBranch, 3) + for (const event of ['pull_request', 'workflow_dispatch']) { + const result = resolveBase(data, { + GITHUB_EVENT_NAME: event, + GITHUB_REF_NAME: 'feature', + GITHUB_BASE_REF: baseBranch, + }) + expect(result.status, result.stderr).toBe(0) + const ref = result.output.slice('ref='.length) + expect(git(data.repo, 'rev-parse', ref)).toBe(data.base) + expect(git(data.repo, 'diff', '--name-only', ref, data.head)).toBe( + 'change-1.ts\nchange-2.ts\nchange-3.ts' + ) + } + } + ) + + it('resolves an ordinary single-commit PR', () => { + const data = ordinaryFixture('staging', 1) + const result = resolveBase(data, { GITHUB_REF_NAME: 'feature' }) + expect(result.status, result.stderr).toBe(0) + expect(result.output).toBe(`ref=${data.base}`) + expect(git(data.repo, 'diff', '--name-only', data.base, data.head)).toBe('change-1.ts') + }) + + it('audits a squash merge into staging against the pre-push staging commit', () => { + const data = ordinaryFixture('staging', 3) + git(data.repo, 'checkout', '--quiet', 'staging') + git(data.repo, 'merge', '--quiet', '--squash', 'feature') + git(data.repo, 'commit', '--quiet', '-m', 'squash feature') + data.head = git(data.repo, 'rev-parse', 'HEAD') + git(data.repo, 'push', '--quiet', 'origin', 'staging') + const result = resolveBase(data, { + GITHUB_EVENT_NAME: 'push', + GITHUB_REF_NAME: 'staging', + GITHUB_BEFORE: data.base, + API_EXIT: '73', + }) + expect(result.status, result.stderr).toBe(0) + expect(result.output).toBe(`ref=${data.base}`) + expect(git(data.repo, 'diff', '--name-only', data.base, data.head)).toBe( + 'change-1.ts\nchange-2.ts\nchange-3.ts' + ) + }) + it('pins the actual PR base for a dispatched merge head instead of its first parent', () => { const data = fixture() const result = resolveBase(data) From d205f64bbbea7674bf35e5702e6c170779b2878c Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 13 Oct 2026 12:31:12 -0700 Subject: [PATCH 104/113] ci: restore upstream audit base selection --- .github/scripts/resolve-audit-base.sh | 42 ----- .github/workflows/checks.yml | 20 ++- .github/workflows/ci.yml | 3 - scripts/audit-base-workflow.test.ts | 242 -------------------------- 4 files changed, 11 insertions(+), 296 deletions(-) delete mode 100644 .github/scripts/resolve-audit-base.sh delete mode 100644 scripts/audit-base-workflow.test.ts diff --git a/.github/scripts/resolve-audit-base.sh b/.github/scripts/resolve-audit-base.sh deleted file mode 100644 index 153e8745498..00000000000 --- a/.github/scripts/resolve-audit-base.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -if [ "$GITHUB_EVENT_NAME" = pull_request ]; then - git fetch --depth=1 origin "$GITHUB_BASE_REF" - echo "ref=origin/$GITHUB_BASE_REF" >> "$GITHUB_OUTPUT" -elif [ "$GITHUB_EVENT_NAME" = workflow_dispatch ]; then - if [ "$GITHUB_REF_TYPE" != branch ]; then - echo 'Manual diff audits require a branch with exactly one open pull request.' >&2 - exit 1 - fi - - pr_pages=$(gh api --method GET "repos/$GITHUB_REPOSITORY/pulls" \ - -f state=open -f "head=${GITHUB_REPOSITORY%%/*}:$GITHUB_REF_NAME" --paginate --slurp) - matching_prs=$(jq -ce --arg repository "$GITHUB_REPOSITORY" --arg branch "$GITHUB_REF_NAME" \ - '[.[][] | select(.state == "open" and .head.ref == $branch and .head.repo.full_name == $repository)]' \ - <<< "$pr_pages") - if [ "$(jq 'length' <<< "$matching_prs")" != 1 ]; then - echo 'Manual diff audits require exactly one open pull request for the dispatched branch.' >&2 - exit 1 - fi - if [ "$(jq -r '.[0].head.sha' <<< "$matching_prs")" != "$GITHUB_SHA" ]; then - echo 'The pull request head changed after dispatch; dispatch again for its current head.' >&2 - exit 1 - fi - - base_ref=$(jq -er '.[0].base.ref | select(type == "string" and length > 0)' <<< "$matching_prs") - if ! git check-ref-format "refs/heads/$base_ref"; then - echo 'The pull request did not provide a valid base branch.' >&2 - exit 1 - fi - git fetch --depth=1 origin "refs/heads/$base_ref" - base_sha=$(git rev-parse --verify 'FETCH_HEAD^{commit}') - echo "ref=$base_sha" >> "$GITHUB_OUTPUT" -elif [ -n "${GITHUB_BEFORE:-}" ] && - [ "$GITHUB_BEFORE" != 0000000000000000000000000000000000000000 ]; then - git fetch --depth=1 origin "$GITHUB_BEFORE" - echo "ref=$GITHUB_BEFORE" >> "$GITHUB_OUTPUT" -else - git fetch --depth=2 origin "$GITHUB_SHA" - echo 'ref=HEAD~1' >> "$GITHUB_OUTPUT" -fi diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index e376f2f5219..0eb511a107e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -524,9 +524,6 @@ jobs: # kept off the test shards so neither waits on the other. lint: name: lint - permissions: - contents: read - pull-requests: read runs-on: *runner-4vcpu timeout-minutes: 15 @@ -616,14 +613,19 @@ jobs: # It is fetched by SHA at depth 1; the audits diff two tips and need no # common ancestry. An all-zero `before` means the branch is new and has no # predecessor to diff, so `HEAD~1` remains the fallback there. - # Manual runs pin the actual base SHA of the dispatched branch's unique open PR. - # A merge commit's first parent does not identify a stacked PR's review base. - name: Resolve base ref for diff-based audits id: audit_base - env: - GH_TOKEN: ${{ github.token }} - GITHUB_BEFORE: ${{ github.event.before }} - run: bash .github/scripts/resolve-audit-base.sh + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + git fetch --depth=1 origin "${{ github.base_ref }}" + echo "ref=origin/${{ github.base_ref }}" >> "$GITHUB_OUTPUT" + elif [ -n "${{ github.event.before }}" ] && + [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then + git fetch --depth=1 origin "${{ github.event.before }}" + echo "ref=${{ github.event.before }}" >> "$GITHUB_OUTPUT" + else + echo "ref=HEAD~1" >> "$GITHUB_OUTPUT" + fi - name: Check block registry invariants run: bun run apps/sim/scripts/check-block-registry.ts "${{ steps.audit_base.outputs.ref }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 750a1b8ce03..c5de7794781 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,9 +33,6 @@ permissions: jobs: checks: name: checks - permissions: - contents: read - pull-requests: read if: github.ref != 'refs/heads/dev' || github.event_name == 'pull_request' uses: ./.github/workflows/checks.yml diff --git a/scripts/audit-base-workflow.test.ts b/scripts/audit-base-workflow.test.ts deleted file mode 100644 index f3d0fabad5f..00000000000 --- a/scripts/audit-base-workflow.test.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { spawnSync } from 'node:child_process' -import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { fileURLToPath } from 'node:url' -import { afterEach, describe, expect, it } from 'vitest' - -const script = fileURLToPath(new URL('../.github/scripts/resolve-audit-base.sh', import.meta.url)) -const fixtures: string[] = [] - -function git(cwd: string, ...args: string[]): string { - const result = spawnSync('git', args, { cwd, encoding: 'utf8' }) - if (result.status !== 0) throw new Error(`git ${args.join(' ')}: ${result.stderr}`) - return result.stdout.trim() -} - -function fixture() { - const root = mkdtempSync(join(tmpdir(), 'audit-base-')) - fixtures.push(root) - const origin = join(root, 'origin.git') - const repo = join(root, 'repo') - git(root, 'init', '--quiet', '--bare', origin) - git(root, 'init', '--quiet', '-b', 'foundation', repo) - git(repo, 'config', 'user.email', 'test@example.com') - git(repo, 'config', 'user.name', 'Test') - git(repo, 'config', 'commit.gpgsign', 'false') - git(repo, 'remote', 'add', 'origin', origin) - git(repo, 'commit', '--quiet', '--allow-empty', '-m', 'initial') - git(repo, 'branch', 'parent') - writeFileSync(join(repo, 'foundation.sql'), 'SELECT 1;\n') - git(repo, 'add', '.') - git(repo, 'commit', '--quiet', '-m', 'foundation') - const before = git(repo, 'rev-parse', 'HEAD') - git(repo, 'checkout', '--quiet', 'parent') - writeFileSync(join(repo, 'upstream.sql'), 'SELECT 2;\n') - git(repo, 'add', '.') - git(repo, 'commit', '--quiet', '-m', 'upstream') - const base = git(repo, 'rev-parse', 'HEAD') - git(repo, 'checkout', '--quiet', 'foundation') - git(repo, 'merge', '--quiet', '--no-ff', 'parent', '-m', 'merge parent') - const head = git(repo, 'rev-parse', 'HEAD') - git(repo, 'push', '--quiet', 'origin', 'parent', 'foundation') - const pr = { - state: 'open', - head: { ref: 'foundation', sha: head, repo: { full_name: 'example/repo' } }, - base: { ref: 'parent', sha: base }, - } - return { root, repo, base, before, head, pr } -} - -function ordinaryFixture(baseBranch: string, commits: number) { - const data = fixture() - git(data.repo, 'checkout', '--quiet', '-b', baseBranch, data.base) - git(data.repo, 'checkout', '--quiet', '-b', 'feature') - for (let index = 1; index <= commits; index++) { - writeFileSync(join(data.repo, `change-${index}.ts`), `export const value = ${index}\n`) - git(data.repo, 'add', '.') - git(data.repo, 'commit', '--quiet', '-m', `change ${index}`) - } - const head = git(data.repo, 'rev-parse', 'HEAD') - git(data.repo, 'push', '--quiet', 'origin', baseBranch, 'feature') - return { - ...data, - head, - pr: { - ...data.pr, - head: { ...data.pr.head, ref: 'feature', sha: head }, - base: { ref: baseBranch, sha: data.base }, - }, - } -} - -function resolveBase(data: ReturnType, overrides: Record = {}) { - const output = join(data.root, 'output') - writeFileSync(output, '') - const result = spawnSync( - 'bash', - [ - '--noprofile', - '--norc', - '-c', - 'gh() { printf "%s" "$PR_RESPONSE"; return "$API_EXIT"; }; source "$AUDIT_SCRIPT"', - ], - { - cwd: data.repo, - encoding: 'utf8', - env: { - ...process.env, - AUDIT_SCRIPT: script, - GITHUB_EVENT_NAME: 'workflow_dispatch', - GITHUB_REPOSITORY: 'example/repo', - GITHUB_REF_TYPE: 'branch', - GITHUB_REF_NAME: 'foundation', - GITHUB_SHA: data.head, - GITHUB_BASE_REF: 'parent', - GITHUB_BEFORE: '', - GITHUB_OUTPUT: output, - PR_RESPONSE: JSON.stringify([[data.pr]]), - API_EXIT: '0', - ...overrides, - }, - } - ) - return { ...result, output: readFileSync(output, 'utf8').trim() } -} - -afterEach(() => { - for (const root of fixtures.splice(0)) rmSync(root, { recursive: true, force: true }) -}) - -describe('diff audit base selection', () => { - it.each(['main', 'staging', 'dev'])( - 'audits every commit of an ordinary PR into %s for both PR and manual events', - (baseBranch) => { - const data = ordinaryFixture(baseBranch, 3) - for (const event of ['pull_request', 'workflow_dispatch']) { - const result = resolveBase(data, { - GITHUB_EVENT_NAME: event, - GITHUB_REF_NAME: 'feature', - GITHUB_BASE_REF: baseBranch, - }) - expect(result.status, result.stderr).toBe(0) - const ref = result.output.slice('ref='.length) - expect(git(data.repo, 'rev-parse', ref)).toBe(data.base) - expect(git(data.repo, 'diff', '--name-only', ref, data.head)).toBe( - 'change-1.ts\nchange-2.ts\nchange-3.ts' - ) - } - } - ) - - it('resolves an ordinary single-commit PR', () => { - const data = ordinaryFixture('staging', 1) - const result = resolveBase(data, { GITHUB_REF_NAME: 'feature' }) - expect(result.status, result.stderr).toBe(0) - expect(result.output).toBe(`ref=${data.base}`) - expect(git(data.repo, 'diff', '--name-only', data.base, data.head)).toBe('change-1.ts') - }) - - it('audits a squash merge into staging against the pre-push staging commit', () => { - const data = ordinaryFixture('staging', 3) - git(data.repo, 'checkout', '--quiet', 'staging') - git(data.repo, 'merge', '--quiet', '--squash', 'feature') - git(data.repo, 'commit', '--quiet', '-m', 'squash feature') - data.head = git(data.repo, 'rev-parse', 'HEAD') - git(data.repo, 'push', '--quiet', 'origin', 'staging') - const result = resolveBase(data, { - GITHUB_EVENT_NAME: 'push', - GITHUB_REF_NAME: 'staging', - GITHUB_BEFORE: data.base, - API_EXIT: '73', - }) - expect(result.status, result.stderr).toBe(0) - expect(result.output).toBe(`ref=${data.base}`) - expect(git(data.repo, 'diff', '--name-only', data.base, data.head)).toBe( - 'change-1.ts\nchange-2.ts\nchange-3.ts' - ) - }) - - it('pins the actual PR base for a dispatched merge head instead of its first parent', () => { - const data = fixture() - const result = resolveBase(data) - expect(result.status, result.stderr).toBe(0) - expect(result.output).toBe(`ref=${data.base}`) - expect(git(data.repo, 'diff', '--name-only', data.base, 'HEAD')).toBe('foundation.sql') - expect(git(data.repo, 'diff', '--name-only', data.before, 'HEAD')).toBe('upstream.sql') - }) - - it('pins the current base branch when the PR snapshot still reports an older base SHA', () => { - const data = fixture() - const cachedBase = git(data.repo, 'rev-parse', `${data.base}^`) - const result = resolveBase(data, { - PR_RESPONSE: JSON.stringify([[{ ...data.pr, base: { ...data.pr.base, sha: cachedBase } }]]), - }) - expect(result.status, result.stderr).toBe(0) - expect(result.output).toBe(`ref=${data.base}`) - expect(git(data.repo, 'rev-parse', 'FETCH_HEAD')).toBe(data.base) - }) - - it('fails closed when multiple open PRs across pages claim the dispatched branch', () => { - const data = fixture() - const result = resolveBase(data, { PR_RESPONSE: JSON.stringify([[data.pr], [data.pr]]) }) - expect(result.status).not.toBe(0) - expect(result.output).toBe('') - }) - - it.each(['missing', 'moved', 'foreign', 'tag', 'api', 'invalid-base', 'fetch'] as const)( - 'does not publish an audit base when resolution is %s', - (failure) => { - const data = fixture() - const overrides: Record = {} - if (failure === 'missing') overrides.PR_RESPONSE = '[[]]' - if (failure === 'moved') overrides.GITHUB_SHA = data.before - if (failure === 'foreign') overrides.GITHUB_REPOSITORY = 'another/repo' - if (failure === 'tag') overrides.GITHUB_REF_TYPE = 'tag' - if (failure === 'api') overrides.API_EXIT = '73' - if (failure === 'invalid-base') - overrides.PR_RESPONSE = JSON.stringify([ - [{ ...data.pr, base: { ref: 'HEAD~1', sha: 'HEAD~1' } }], - ]) - if (failure === 'fetch') - git(data.repo, 'remote', 'set-url', 'origin', join(data.root, 'missing.git')) - const result = resolveBase(data, overrides) - expect(result.status).not.toBe(0) - expect(result.output).toBe('') - } - ) - - it('keeps the normal PR base branch even if no API association is available', () => { - const data = fixture() - const result = resolveBase(data, { GITHUB_EVENT_NAME: 'pull_request', API_EXIT: '73' }) - expect(result.status, result.stderr).toBe(0) - expect(result.output).toBe('ref=origin/parent') - expect(git(data.repo, 'rev-parse', 'origin/parent')).toBe(data.base) - }) - - it('keeps the pre-push SHA for an existing branch', () => { - const data = fixture() - const result = resolveBase(data, { GITHUB_EVENT_NAME: 'push', GITHUB_BEFORE: data.before }) - expect(result.status, result.stderr).toBe(0) - expect(result.output).toBe(`ref=${data.before}`) - }) - - it('keeps the first-parent fallback for a new branch push', () => { - const data = fixture() - const shallow = join(data.root, 'shallow') - git( - data.root, - 'clone', - '--quiet', - '--depth=1', - '--branch=foundation', - `file://${join(data.root, 'origin.git')}`, - shallow - ) - data.repo = shallow - const result = resolveBase(data, { GITHUB_EVENT_NAME: 'push', GITHUB_BEFORE: '0'.repeat(40) }) - expect(result.status, result.stderr).toBe(0) - expect(result.output).toBe('ref=HEAD~1') - expect(git(data.repo, 'rev-parse', 'HEAD~1')).toBe(data.before) - }) -}) From 3ff8a4a44e73c05f2c2d6f2c71d94c3facddda89 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 13 Oct 2026 18:22:42 -0700 Subject: [PATCH 105/113] fix(db): retry public share backfill contention without losing progress --- .../db/public-share-ownership.integration.ts | 223 ++++++++++++++++-- .../0034_backfill_public_share_entities.ts | 45 +++- 2 files changed, 238 insertions(+), 30 deletions(-) diff --git a/packages/db/public-share-ownership.integration.ts b/packages/db/public-share-ownership.integration.ts index 3036a7b1c37..09dedac7c67 100644 --- a/packages/db/public-share-ownership.integration.ts +++ b/packages/db/public-share-ownership.integration.ts @@ -1,6 +1,11 @@ import { readFileSync } from 'node:fs' import { mkdir, writeFile } from 'node:fs/promises' import { dirname } from 'node:path' +import { + backfillPublicShareEntities, + backfillPublicShareEntitiesMigration, +} from '@sim/db/script-migrations/0034_backfill_public_share_entities' +import { runScriptMigrations } from '@sim/db/script-migrations/index' import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure' import { createDeferred } from '@sim/testing/helpers/deferred' import { getErrorMessage } from '@sim/utils/errors' @@ -19,22 +24,26 @@ const migrations = [ ].map((name) => readFileSync(new URL(`./migrations/${name}`, import.meta.url), 'utf8')) const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = [] -function check(name: string, run: () => Promise) { - it(name, async () => { - const started = performance.now() - try { - await run() - checks.push({ name, status: 'passed', durationMs: performance.now() - started }) - } catch (error) { - checks.push({ - name, - status: 'failed', - durationMs: performance.now() - started, - error: getErrorMessage(error), - }) - throw error - } - }, 10_000) +function check(name: string, run: () => Promise, timeout = 10_000) { + it( + name, + async () => { + const started = performance.now() + try { + await run() + checks.push({ name, status: 'passed', durationMs: performance.now() - started }) + } catch (error) { + checks.push({ + name, + status: 'failed', + durationMs: performance.now() - started, + error: getErrorMessage(error), + }) + throw error + } + }, + timeout + ) } describe('Public share canonical ownership in PostgreSQL', () => { @@ -108,6 +117,8 @@ describe('Public share canonical ownership in PostgreSQL', () => { workspace_id text NOT NULL REFERENCES workspace(id) ON DELETE CASCADE, created_by text REFERENCES "user"(id) ON DELETE SET NULL, token text NOT NULL UNIQUE, is_active boolean NOT NULL DEFAULT true, + auth_type text NOT NULL DEFAULT 'password', password text DEFAULT 'fixture-hash', + allowed_emails json DEFAULT '["reader@example.test"]', updated_at timestamp NOT NULL DEFAULT '2026-10-03 01:02:03.456', UNIQUE(resource_type, resource_id) )` @@ -125,10 +136,11 @@ describe('Public share canonical ownership in PostgreSQL', () => { } await sql`CREATE TABLE workspace_file_secret_provenance (file_id text PRIMARY KEY, content_updated_at timestamp NOT NULL)` for (const migration of migrations) await applyMigration(migration) + await sql`CREATE TABLE script_migrations (name text PRIMARY KEY, applied_at timestamptz DEFAULT now())` }) beforeEach(async () => { - await sql`TRUNCATE workspace_file_secret_provenance, public_share, workspace_file_version, workspace_files, folder, project, workspace, organization, "user"` + await sql`TRUNCATE script_migrations, workspace_file_secret_provenance, public_share, workspace_file_version, workspace_files, folder, project, workspace, organization, "user"` await sql`INSERT INTO "user" VALUES ('user-a'), ('user-b')` await sql`INSERT INTO workspace VALUES ('workspace-a'), ('workspace-b'), ('same-id')` await sql`INSERT INTO organization VALUES ('organization-a')` @@ -304,9 +316,6 @@ describe('Public share canonical ownership in PostgreSQL', () => { } const before = await sql`SELECT id, resource_type, resource_id, workspace_id, token, is_active, updated_at FROM public_share ORDER BY id` - const { backfillPublicShareEntities } = await import( - './script-migrations/0034_backfill_public_share_entities' - ) const first = await backfillPublicShareEntities(sql) expect(first).toMatchObject({ backfilled: 502, unresolved: 3 }) expect(first.sampleShareIds.sort()).toEqual(['foreign', 'missing', 'unknown']) @@ -322,6 +331,180 @@ describe('Public share canonical ownership in PostgreSQL', () => { } ) + async function seedLegacyFileShares() { + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, original_name, deleted_at) + SELECT 'file-' || lpad(n::text, 4, '0'), 'workspace', 'user-a', 'workspace-a', + 'file-' || n, CASE WHEN n % 10 = 0 THEN NULL ELSE timestamp '2026-10-01 01:02:03.456' END + FROM generate_series(1, 1001) n` + await sql`ALTER TABLE public_share DISABLE TRIGGER public_share_entity_binding` + try { + await sql`INSERT INTO public_share (id, resource_type, resource_id, workspace_id, token, is_active) + SELECT 'share-' || lpad(n::text, 4, '0'), 'file', 'file-' || lpad(n::text, 4, '0'), + 'workspace-a', 'token-' || n, n % 2 = 0 FROM generate_series(1, 1001) n` + } finally { + await sql`ALTER TABLE public_share ENABLE TRIGGER public_share_entity_binding` + } + } + + async function retainedFileShareState() { + return sql`SELECT + (SELECT md5(string_agg((to_jsonb(s) - 'entity_type' - 'entity_id')::text, '' ORDER BY id)) + FROM public_share s WHERE id LIKE 'share-%') AS shares, + (SELECT md5(string_agg(to_jsonb(f)::text, '' ORDER BY id)) + FROM workspace_files f WHERE id LIKE 'file-%') AS files` + } + + async function committedShareCount() { + const [row] = await sql<{ count: number }[]>`SELECT count(*)::integer AS count + FROM public_share WHERE entity_type = 'workspace' AND entity_id = workspace_id` + return row.count + } + + for (const target of ['file', 'share'] as const) { + for (const persistent of [false, true]) { + check( + `backfill ${persistent ? 'stops after bounded retries under persistent' : 'recovers after temporary'} ${target} lock contention without losing committed batches`, + async () => { + await seedLegacyFileShares() + const before = await retainedFileShareState() + const firstRollback = createDeferred() + let rollbacks = 0 + const worker = postgres(databaseUrl, { + max: 1, + onnotice: () => undefined, + connection: { search_path: schemaName }, + debug: (_connection, query) => { + if (query.toLowerCase() === 'rollback') { + rollbacks += 1 + firstRollback.resolve() + } + }, + }) + const held = createDeferred() + const release = createDeferred() + const holding = sql.begin(async (tx) => { + if (target === 'file') + await tx`SELECT id FROM workspace_files WHERE id = 'file-0501' FOR UPDATE` + else await tx`SELECT id FROM public_share WHERE id = 'share-0501' FOR UPDATE` + held.resolve() + await release.promise + }) + await Promise.race([held.promise, holding]) + const running = runScriptMigrations(worker, [backfillPublicShareEntitiesMigration]).then( + () => null, + (error: unknown) => error + ) + try { + if (!persistent) { + await Promise.race([firstRollback.promise, running]) + expect(await committedShareCount()).toBe(500) + expect(await sql`SELECT name FROM script_migrations`).toEqual([]) + release.resolve() + await holding + } + const error = await running + if (persistent) { + expect(error).toMatchObject({ code: '55P03' }) + expect(rollbacks).toBe(3) + expect(await committedShareCount()).toBe(500) + expect(await sql`SELECT name FROM script_migrations`).toEqual([]) + release.resolve() + await holding + await runScriptMigrations(worker, [backfillPublicShareEntitiesMigration]) + } else { + expect(error).toBeNull() + expect(rollbacks).toBe(1) + } + expect(await committedShareCount()).toBe(1001) + expect(await retainedFileShareState()).toEqual(before) + const receipts = await sql`SELECT * FROM script_migrations` + expect(receipts).toHaveLength(1) + expect(receipts[0].name).toBe(backfillPublicShareEntitiesMigration.name) + await runScriptMigrations(worker, [backfillPublicShareEntitiesMigration]) + expect(await sql`SELECT * FROM script_migrations`).toEqual(receipts) + } finally { + release.resolve() + await holding + await running + await worker.end({ timeout: 1 }) + } + }, + 40_000 + ) + } + } + + check( + 'cancelled backfill rolls back its incomplete batch, preserves progress, and resumes without missing concurrent legacy inserts', + async () => { + await seedLegacyFileShares() + const before = await retainedFileShareState() + await sql`CREATE SEQUENCE backfill_write_attempts` + await sql`CREATE FUNCTION pause_backfill_batch() RETURNS trigger LANGUAGE plpgsql AS $$ + BEGIN + PERFORM nextval('backfill_write_attempts'); + IF NEW.id = 'share-0750' THEN PERFORM pg_advisory_xact_lock(86090034); END IF; + RETURN NEW; + END + $$` + await sql`CREATE TRIGGER pause_backfill_batch BEFORE UPDATE ON public_share + FOR EACH ROW EXECUTE FUNCTION pause_backfill_batch()` + const worker = postgres(databaseUrl, { + max: 1, + onnotice: () => undefined, + connection: { search_path: schemaName }, + }) + const [connection] = await worker<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + const held = createDeferred() + const release = createDeferred() + const holding = sql.begin(async (tx) => { + await tx`SELECT pg_advisory_xact_lock(86090034)` + const [holder] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + held.resolve(holder.pid) + await release.promise + }) + const holderPid = await Promise.race([held.promise, holding.then(() => 0)]) + const running = runScriptMigrations(worker, [backfillPublicShareEntitiesMigration]).then( + () => null, + (error: unknown) => error + ) + try { + expect(await waitForDatabaseLock(connection.pid, holderPid)).toBe(true) + expect(await committedShareCount()).toBe(500) + const [attempts] = await sql< + { count: number }[] + >`SELECT last_value::integer AS count FROM backfill_write_attempts` + expect(attempts.count).toBeGreaterThan(501) + await sql`INSERT INTO public_share (id, resource_type, resource_id, workspace_id, token) + VALUES ('before-cursor', 'folder', 'concurrent-folder', 'workspace-a', 'concurrent-token')` + expect( + await sql`SELECT entity_type, entity_id FROM public_share WHERE id = 'before-cursor'` + ).toEqual([{ entity_type: 'workspace', entity_id: 'workspace-a' }]) + await sql`SELECT pg_cancel_backend(${connection.pid})` + expect(await running).toMatchObject({ code: '57014' }) + expect(await committedShareCount()).toBe(501) + expect(await sql`SELECT name FROM script_migrations`).toEqual([]) + expect(await retainedFileShareState()).toEqual(before) + release.resolve() + await holding + await runScriptMigrations(worker, [backfillPublicShareEntitiesMigration]) + expect(await committedShareCount()).toBe(1002) + expect(await retainedFileShareState()).toEqual(before) + expect(await sql`SELECT name FROM script_migrations`).toEqual([ + { name: backfillPublicShareEntitiesMigration.name }, + ]) + } finally { + release.resolve() + await holding + await running + await worker.end({ timeout: 1 }) + await sql`DROP TRIGGER pause_backfill_batch ON public_share` + await sql`DROP FUNCTION pause_backfill_batch()` + await sql`DROP SEQUENCE backfill_write_attempts` + } + } + ) + for (const isolation of ['read committed', 'repeatable read'] as const) { check( `${isolation}: file deletion waits for share insertion and cannot leave an orphan`, diff --git a/packages/db/script-migrations/0034_backfill_public_share_entities.ts b/packages/db/script-migrations/0034_backfill_public_share_entities.ts index ad5a207a65f..0291227e183 100644 --- a/packages/db/script-migrations/0034_backfill_public_share_entities.ts +++ b/packages/db/script-migrations/0034_backfill_public_share_entities.ts @@ -1,9 +1,13 @@ import type { ScriptMigration } from '@sim/db/script-migrations/types' import { createLogger } from '@sim/logger' +import { getTransientDatabaseFailure } from '@sim/utils/errors' +import { sleep } from '@sim/utils/helpers' +import { backoffWithJitter } from '@sim/utils/retry' import type { Sql } from 'postgres' const logger = createLogger('PublicShareEntityBackfill') const BATCH_SIZE = 500 +const MAX_BATCH_ATTEMPTS = 3 const MAX_REPAIR_SAMPLES = 10 /** Stamps verified legacy share owners without repairing ambiguous targets or changing tokens. */ @@ -18,21 +22,23 @@ export async function backfillPublicShareEntities(sql: Sql) { ORDER BY id LIMIT ${BATCH_SIZE} ` if (!rows.length) break - afterId = rows[rows.length - 1].id const ids = rows.map((row) => row.id) const fileIds = [ ...new Set(rows.filter((row) => row.resource_type === 'file').map((row) => row.resource_id)), ].sort() - const updated = await sql.begin(async (tx) => { - await tx`SET LOCAL lock_timeout = '5s'` - await tx`SET LOCAL statement_timeout = '30s'` - // Target locks precede share locks, as they do for insertion and target retirement. - if (fileIds.length) - await tx` + const started = performance.now() + for (let attempt = 1; ; attempt++) { + try { + const updated = await sql.begin(async (tx) => { + await tx`SELECT set_config('lock_timeout', '5s', true), + set_config('statement_timeout', '30s', true)` + // Target locks precede share locks, as they do for insertion and target retirement. + if (fileIds.length) + await tx` SELECT id FROM workspace_files WHERE id = ANY(${fileIds}::text[]) ORDER BY id FOR UPDATE ` - return tx<{ id: string }[]>` + return tx<{ id: string }[]>` UPDATE public_share share SET entity_type = 'workspace', entity_id = share.workspace_id WHERE share.id = ANY(${ids}::text[]) AND share.entity_type IS NULL AND share.entity_id IS NULL @@ -48,8 +54,27 @@ export async function backfillPublicShareEntities(sql: Sql) { )) RETURNING share.id ` - }) - backfilled += updated.length + }) + backfilled += updated.length + afterId = rows[rows.length - 1].id + logger.info('Public share owner batch committed', { + candidates: rows.length, + backfilled: updated.length, + attempts: attempt, + durationMs: Math.round(performance.now() - started), + }) + break + } catch (error) { + const failure = getTransientDatabaseFailure(error) + if (attempt >= MAX_BATCH_ATTEMPTS || (failure !== 'capacity' && failure !== 'conflict')) + throw error + logger.warn('Retrying public share owner batch after transient contention', { + attempt, + failure, + }) + await sleep(backoffWithJitter(attempt, null, { baseMs: 250, maxMs: 1000 })) + } + } } let unresolved = 0 From 3bed99bb5742f589e367afa6ed50f4a34694f69f Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 13 Oct 2026 19:00:21 -0700 Subject: [PATCH 106/113] refactor(db): fold creator lifetime guard into file ownership migration --- apps/sim/lib/workspace-files/search/README.md | 2 +- .../search/chunks.integration.ts | 2 +- .../search/dispatcher.integration.ts | 2 +- .../db/file-creator-lifetime.integration.ts | 12 +- .../db/file-entity-ownership.integration.ts | 2 +- ...le-folder-version-ownership.integration.ts | 3 +- .../0423_file_folder_version_ownership.sql | 11 +- .../migrations/0424_file_creator_lifetime.sql | 14 - ... => 0424_project_file_upload_sessions.sql} | 0 ...=> 0425_public_share_entity_ownership.sql} | 0 ...e.sql => 0426_file_search_owner_scope.sql} | 0 ...ng.sql => 0427_file_workspace_binding.sql} | 0 .../db/migrations/meta/0424_snapshot.json | 3 +- .../db/migrations/meta/0425_snapshot.json | 52 +- .../db/migrations/meta/0426_snapshot.json | 379 +- .../db/migrations/meta/0427_snapshot.json | 8 +- .../db/migrations/meta/0428_snapshot.json | 31327 ---------------- packages/db/migrations/meta/_journal.json | 21 +- .../db/public-share-ownership.integration.ts | 3 +- ...le-ownership-reconciliation.integration.ts | 2 +- .../db/scripts/reconcile-file-ownership.ts | 5 +- 21 files changed, 457 insertions(+), 31391 deletions(-) delete mode 100644 packages/db/migrations/0424_file_creator_lifetime.sql rename packages/db/migrations/{0425_project_file_upload_sessions.sql => 0424_project_file_upload_sessions.sql} (100%) rename packages/db/migrations/{0426_public_share_entity_ownership.sql => 0425_public_share_entity_ownership.sql} (100%) rename packages/db/migrations/{0427_file_search_owner_scope.sql => 0426_file_search_owner_scope.sql} (100%) rename packages/db/migrations/{0428_file_workspace_binding.sql => 0427_file_workspace_binding.sql} (100%) delete mode 100644 packages/db/migrations/meta/0428_snapshot.json diff --git a/apps/sim/lib/workspace-files/search/README.md b/apps/sim/lib/workspace-files/search/README.md index 9d3bc5ac273..48e498ae0df 100644 --- a/apps/sim/lib/workspace-files/search/README.md +++ b/apps/sim/lib/workspace-files/search/README.md @@ -69,7 +69,7 @@ The design uses PostgreSQL's documented [TOAST behavior](https://www.postgresql. ### File owner expansion -Migration `0427_file_search_owner_scope.sql` adds canonical workspace/Project ownership to the existing build, revision, and chunk pipeline. Legacy workspace inserts are translated by triggers, and the existing workspace trigram index remains usable. Project rows use a separate owner-scoped trigram index on the same chunk table. Organizations and personal users are not enabled search owners. +Migration `0426_file_search_owner_scope.sql` adds canonical workspace/Project ownership to the existing build, revision, and chunk pipeline. Legacy workspace inserts are translated by triggers, and the existing workspace trigram index remains usable. Project rows use a separate owner-scoped trigram index on the same chunk table. Organizations and personal users are not enabled search owners. The new `file_search_dispatch_queue` replaces the workspace-keyed scheduler because older workers cannot decode a Project without a workspace. During expansion, workspace mutations enqueue both queues, while the new dispatcher reads the owner queue. Both deployments share the dispatcher lock, revision claim token, build lease, and publication fence. Drain old app dispatchers and Trigger workers/retries before enabling Project dispatch. Retire the legacy queue and its dual enqueue in a later contract deploy; they are not a second indexing pipeline. The `file-search-owners-v3` cursor performs bounded owner/file keyset backfill and hourly reconciliation without rewriting extracted text in the migration. diff --git a/apps/sim/lib/workspace-files/search/chunks.integration.ts b/apps/sim/lib/workspace-files/search/chunks.integration.ts index c63f9a1472b..03d2fcd9a2f 100644 --- a/apps/sim/lib/workspace-files/search/chunks.integration.ts +++ b/apps/sim/lib/workspace-files/search/chunks.integration.ts @@ -173,7 +173,7 @@ describe('chunked workspace file search on PostgreSQL', () => { '0359_workspace_file_search_chunks.sql', ginWriteMigration, '0382_workspace_file_search_dispatch_handoff.sql', - '0427_file_search_owner_scope.sql', + '0426_file_search_owner_scope.sql', ]) { await applyMigration(migration) } diff --git a/apps/sim/lib/workspace-files/search/dispatcher.integration.ts b/apps/sim/lib/workspace-files/search/dispatcher.integration.ts index 2fb9e117a17..b820bd615ba 100644 --- a/apps/sim/lib/workspace-files/search/dispatcher.integration.ts +++ b/apps/sim/lib/workspace-files/search/dispatcher.integration.ts @@ -70,7 +70,7 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { }) ) const migration = readFileSync( - resolve(process.cwd(), '../../packages/db/migrations/0427_file_search_owner_scope.sql'), + resolve(process.cwd(), '../../packages/db/migrations/0426_file_search_owner_scope.sql'), 'utf8' ).replaceAll('"public".', `"${schemaName}".`) const ownerIndexes = [ diff --git a/packages/db/file-creator-lifetime.integration.ts b/packages/db/file-creator-lifetime.integration.ts index a6fff1ee3e1..2b9bd062cc5 100644 --- a/packages/db/file-creator-lifetime.integration.ts +++ b/packages/db/file-creator-lifetime.integration.ts @@ -8,11 +8,9 @@ import postgres, { type Sql } from 'postgres' import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' const databaseUrl = readTestDatabaseUrl() -const migrations = [ - '0422_file_entity_ownership.sql', - '0423_file_folder_version_ownership.sql', - '0424_file_creator_lifetime.sql', -].map((name) => readFileSync(new URL(`./migrations/${name}`, import.meta.url), 'utf8')) +const migrations = ['0422_file_entity_ownership.sql', '0423_file_folder_version_ownership.sql'].map( + (name) => readFileSync(new URL(`./migrations/${name}`, import.meta.url), 'utf8') +) const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = [] function check(name: string, run: () => Promise) { @@ -157,8 +155,8 @@ describe('Shared file creator lifetime in PostgreSQL', () => { check( 'creator guard can be replayed without weakening existing resource protection', async () => { - await applyMigration(migrations[2]) - await applyMigration(migrations[2]) + await applyMigration(migrations[1]) + await applyMigration(migrations[1]) await sql`INSERT INTO folder (id,name,user_id,resource_type,project_id,deleted_at) VALUES ('folder','Archived','user-a','file','project-a',now())` await expect(sql`DELETE FROM "user" WHERE id='user-a'`).rejects.toMatchObject({ code: '23514', diff --git a/packages/db/file-entity-ownership.integration.ts b/packages/db/file-entity-ownership.integration.ts index 02f7e826f2d..9923bfda6c4 100644 --- a/packages/db/file-entity-ownership.integration.ts +++ b/packages/db/file-entity-ownership.integration.ts @@ -15,7 +15,7 @@ const migration = readFileSync( ) const workspaceBindingMigration = readFileSync( - new URL('./migrations/0428_file_workspace_binding.sql', import.meta.url), + new URL('./migrations/0427_file_workspace_binding.sql', import.meta.url), 'utf8' ) diff --git a/packages/db/file-folder-version-ownership.integration.ts b/packages/db/file-folder-version-ownership.integration.ts index 87822fe6572..6d30e6a5710 100644 --- a/packages/db/file-folder-version-ownership.integration.ts +++ b/packages/db/file-folder-version-ownership.integration.ts @@ -276,7 +276,8 @@ describe('file folder and version ownership in PostgreSQL', () => { code: '23503', }) await expect(sql`DELETE FROM "user" WHERE id = 'user-a'`).rejects.toMatchObject({ - code: '23503', + code: '23514', + constraint_name: 'project_resource_creator_handoff', }) await sql`UPDATE folder SET user_id = 'user-b' WHERE id = 'project-folder'` await sql`DELETE FROM "user" WHERE id = 'user-a'` diff --git a/packages/db/migrations/0423_file_folder_version_ownership.sql b/packages/db/migrations/0423_file_folder_version_ownership.sql index 66d2fda5e8b..e1f3bcc4c4e 100644 --- a/packages/db/migrations/0423_file_folder_version_ownership.sql +++ b/packages/db/migrations/0423_file_folder_version_ownership.sql @@ -165,14 +165,17 @@ FOR EACH ROW EXECUTE FUNCTION workspace_file_version_owner_match();--> statement CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN - IF EXISTS (SELECT 1 FROM workspace_files WHERE project_id IS NOT NULL AND user_id = OLD.id) - OR EXISTS (SELECT 1 FROM folder WHERE project_id IS NOT NULL AND user_id = OLD.id) THEN - RAISE EXCEPTION 'Project file and folder attribution must be reassigned or retired before deleting its creator' - USING ERRCODE = '23503'; + IF EXISTS (SELECT 1 FROM workspace_files WHERE user_id = OLD.id AND project_id IS NOT NULL) + OR EXISTS (SELECT 1 FROM folder WHERE user_id = OLD.id AND project_id IS NOT NULL) THEN + RAISE EXCEPTION 'Project resource creators must be handed to a successor before account deletion' USING ERRCODE = '23514', CONSTRAINT = 'project_resource_creator_handoff'; END IF; RETURN OLD; END; $$;--> statement-breakpoint +DROP TRIGGER IF EXISTS project_file_uploader_delete_guard ON "user";--> statement-breakpoint +CREATE TRIGGER project_file_uploader_delete_guard +BEFORE DELETE ON "user" +FOR EACH ROW EXECUTE FUNCTION project_file_uploader_delete_guard();--> statement-breakpoint COMMIT;--> statement-breakpoint SET lock_timeout = 0;--> statement-breakpoint DROP INDEX CONCURRENTLY IF EXISTS "folder_project_id_idx";--> statement-breakpoint diff --git a/packages/db/migrations/0424_file_creator_lifetime.sql b/packages/db/migrations/0424_file_creator_lifetime.sql deleted file mode 100644 index 5ded57ae704..00000000000 --- a/packages/db/migrations/0424_file_creator_lifetime.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() -RETURNS trigger LANGUAGE plpgsql AS $$ -BEGIN - IF EXISTS (SELECT 1 FROM workspace_files WHERE user_id = OLD.id AND project_id IS NOT NULL) - OR EXISTS (SELECT 1 FROM folder WHERE user_id = OLD.id AND project_id IS NOT NULL) THEN - RAISE EXCEPTION 'Project resource creators must be handed to a successor before account deletion' USING ERRCODE = '23514', CONSTRAINT = 'project_resource_creator_handoff'; - END IF; - RETURN OLD; -END; -$$;--> statement-breakpoint -DROP TRIGGER IF EXISTS project_file_uploader_delete_guard ON "user";--> statement-breakpoint -CREATE TRIGGER project_file_uploader_delete_guard -BEFORE DELETE ON "user" -FOR EACH ROW EXECUTE FUNCTION project_file_uploader_delete_guard(); diff --git a/packages/db/migrations/0425_project_file_upload_sessions.sql b/packages/db/migrations/0424_project_file_upload_sessions.sql similarity index 100% rename from packages/db/migrations/0425_project_file_upload_sessions.sql rename to packages/db/migrations/0424_project_file_upload_sessions.sql diff --git a/packages/db/migrations/0426_public_share_entity_ownership.sql b/packages/db/migrations/0425_public_share_entity_ownership.sql similarity index 100% rename from packages/db/migrations/0426_public_share_entity_ownership.sql rename to packages/db/migrations/0425_public_share_entity_ownership.sql diff --git a/packages/db/migrations/0427_file_search_owner_scope.sql b/packages/db/migrations/0426_file_search_owner_scope.sql similarity index 100% rename from packages/db/migrations/0427_file_search_owner_scope.sql rename to packages/db/migrations/0426_file_search_owner_scope.sql diff --git a/packages/db/migrations/0428_file_workspace_binding.sql b/packages/db/migrations/0427_file_workspace_binding.sql similarity index 100% rename from packages/db/migrations/0428_file_workspace_binding.sql rename to packages/db/migrations/0427_file_workspace_binding.sql diff --git a/packages/db/migrations/meta/0424_snapshot.json b/packages/db/migrations/meta/0424_snapshot.json index ec00bdae70d..6f464c0f4cc 100644 --- a/packages/db/migrations/meta/0424_snapshot.json +++ b/packages/db/migrations/meta/0424_snapshot.json @@ -1,5 +1,5 @@ { - "id": "007d0d81-be3a-443a-a49b-d7c1317b43ec", + "id": "9df5563b-fff7-4d4c-8774-aec5cbd2ebfe", "prevId": "2b2df413-017e-4a63-8dc6-58cb5c4188a6", "version": "7", "dialect": "postgresql", @@ -30801,6 +30801,7 @@ "schema": "public", "values": [ "workspace_file", + "project_file", "table_import", "knowledge_document", "profile_picture", diff --git a/packages/db/migrations/meta/0425_snapshot.json b/packages/db/migrations/meta/0425_snapshot.json index fa4eb5e3e34..fdcfb8745d1 100644 --- a/packages/db/migrations/meta/0425_snapshot.json +++ b/packages/db/migrations/meta/0425_snapshot.json @@ -1,6 +1,6 @@ { - "id": "9df5563b-fff7-4d4c-8774-aec5cbd2ebfe", - "prevId": "007d0d81-be3a-443a-a49b-d7c1317b43ec", + "id": "6127dc99-6e8f-4842-9a28-7805522e406d", + "prevId": "9df5563b-fff7-4d4c-8774-aec5cbd2ebfe", "version": "7", "dialect": "postgresql", "tables": { @@ -17449,7 +17449,7 @@ "name": "workspace_id", "type": "text", "primaryKey": false, - "notNull": true + "notNull": false }, "created_by": { "name": "created_by", @@ -17503,6 +17503,18 @@ "primaryKey": false, "notNull": true, "default": "now()" + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { @@ -17571,6 +17583,33 @@ "concurrently": false, "method": "btree", "with": {} + }, + "public_share_entity_id_idx": { + "name": "public_share_entity_id_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -17596,7 +17635,12 @@ "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, - "checkConstraints": {}, + "checkConstraints": { + "public_share_entity_binding_check": { + "name": "public_share_entity_binding_check", + "value": "\n (\"public_share\".\"entity_type\" IS NULL AND \"public_share\".\"entity_id\" IS NULL AND \"public_share\".\"workspace_id\" IS NOT NULL)\n OR (\"public_share\".\"entity_type\" IS NOT NULL AND \"public_share\".\"entity_id\" IS NOT NULL AND char_length(\"public_share\".\"entity_id\") > 0\n AND ((\"public_share\".\"entity_type\" = 'workspace' AND \"public_share\".\"workspace_id\" IS NOT NULL AND \"public_share\".\"entity_id\" = \"public_share\".\"workspace_id\")\n OR (\"public_share\".\"entity_type\" = 'project' AND \"public_share\".\"workspace_id\" IS NULL AND \"public_share\".\"resource_type\" = 'file')))\n " + } + }, "isRLSEnabled": false }, "public.rate_limit_bucket": { diff --git a/packages/db/migrations/meta/0426_snapshot.json b/packages/db/migrations/meta/0426_snapshot.json index fdcfb8745d1..166955bc856 100644 --- a/packages/db/migrations/meta/0426_snapshot.json +++ b/packages/db/migrations/meta/0426_snapshot.json @@ -1,6 +1,6 @@ { - "id": "6127dc99-6e8f-4842-9a28-7805522e406d", - "prevId": "9df5563b-fff7-4d4c-8774-aec5cbd2ebfe", + "id": "ec46a436-88d2-4e86-a5f5-87711de5b966", + "prevId": "6127dc99-6e8f-4842-9a28-7805522e406d", "version": "7", "dialect": "postgresql", "tables": { @@ -27913,6 +27913,18 @@ "primaryKey": false, "notNull": true, "default": "now()" + }, + "after_entity_type": { + "name": "after_entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "after_entity_id": { + "name": "after_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -27943,7 +27955,7 @@ "name": "workspace_id", "type": "text", "primaryKey": false, - "notNull": true + "notNull": false }, "source_content_updated_at": { "name": "source_content_updated_at", @@ -27963,6 +27975,24 @@ "type": "timestamp", "primaryKey": false, "notNull": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "artifact_key": { + "name": "artifact_key", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { @@ -28008,7 +28038,12 @@ "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, - "checkConstraints": {}, + "checkConstraints": { + "file_search_build_owner_check": { + "name": "file_search_build_owner_check", + "value": "(\"workspace_file_search_build\".\"entity_type\" IS NULL AND \"workspace_file_search_build\".\"entity_id\" IS NULL AND \"workspace_file_search_build\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_build\".\"entity_type\" = 'workspace' AND \"workspace_file_search_build\".\"entity_id\" = \"workspace_file_search_build\".\"workspace_id\") OR (\"workspace_file_search_build\".\"entity_type\" = 'project' AND length(\"workspace_file_search_build\".\"entity_id\") > 0 AND \"workspace_file_search_build\".\"workspace_id\" IS NULL)" + } + }, "isRLSEnabled": false }, "public.workspace_file_search_chunk": { @@ -28025,7 +28060,7 @@ "name": "workspace_id", "type": "text", "primaryKey": false, - "notNull": true + "notNull": false }, "ordinal": { "name": "ordinal", @@ -28057,6 +28092,18 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { @@ -28111,6 +28158,39 @@ "with": { "fastupdate": "off" } + }, + "file_search_chunk_owner_content_idx": { + "name": "file_search_chunk_owner_content_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "text_ops" + }, + { + "expression": "content", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "gin_trgm_ops" + } + ], + "isUnique": false, + "where": "\"workspace_file_search_chunk\".\"entity_type\" = 'project'", + "concurrently": true, + "method": "gin", + "with": { + "fastupdate": "off" + } } }, "foreignKeys": { @@ -28140,6 +28220,10 @@ "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" + }, + "file_search_chunk_owner_check": { + "name": "file_search_chunk_owner_check", + "value": "(\"workspace_file_search_chunk\".\"entity_type\" IS NULL AND \"workspace_file_search_chunk\".\"entity_id\" IS NULL AND \"workspace_file_search_chunk\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_chunk\".\"entity_type\" = 'workspace' AND \"workspace_file_search_chunk\".\"entity_id\" = \"workspace_file_search_chunk\".\"workspace_id\") OR (\"workspace_file_search_chunk\".\"entity_type\" = 'project' AND length(\"workspace_file_search_chunk\".\"entity_id\") > 0 AND \"workspace_file_search_chunk\".\"workspace_id\" IS NULL)" } }, "isRLSEnabled": false @@ -28429,7 +28513,7 @@ "name": "workspace_id", "type": "text", "primaryKey": false, - "notNull": true + "notNull": false }, "source_content_updated_at": { "name": "source_content_updated_at", @@ -28496,6 +28580,18 @@ "primaryKey": false, "notNull": true, "default": "now()" + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": { @@ -28590,6 +28686,79 @@ "concurrently": false, "method": "btree", "with": {} + }, + "file_search_revision_owner_pending_idx": { + "name": "file_search_revision_owner_pending_idx", + "columns": [ + { + "expression": "coalesce(\"entity_type\", 'workspace')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"entity_id\", \"workspace_id\")", + "asc": true, + "isExpression": 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": true, + "method": "btree", + "with": {} + }, + "file_search_revision_owner_status_idx": { + "name": "file_search_revision_owner_status_idx", + "columns": [ + { + "expression": "coalesce(\"entity_type\", 'workspace')", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"entity_id\", \"workspace_id\")", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -28615,7 +28784,12 @@ "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, - "checkConstraints": {}, + "checkConstraints": { + "file_search_revision_owner_check": { + "name": "file_search_revision_owner_check", + "value": "(\"workspace_file_search_revision\".\"entity_type\" IS NULL AND \"workspace_file_search_revision\".\"entity_id\" IS NULL AND \"workspace_file_search_revision\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_revision\".\"entity_type\" = 'workspace' AND \"workspace_file_search_revision\".\"entity_id\" = \"workspace_file_search_revision\".\"workspace_id\") OR (\"workspace_file_search_revision\".\"entity_type\" = 'project' AND length(\"workspace_file_search_revision\".\"entity_id\") > 0 AND \"workspace_file_search_revision\".\"workspace_id\" IS NULL)" + } + }, "isRLSEnabled": false }, "public.workspace_file_search_segment": { @@ -29467,6 +29641,34 @@ "concurrently": true, "method": "btree", "with": {} + }, + "workspace_files_search_owner_keyset_idx": { + "name": "workspace_files_search_owner_keyset_idx", + "columns": [ + { + "expression": "(CASE WHEN \"project_id\" IS NOT NULL THEN 'project' ELSE 'workspace' END)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"project_id\", \"workspace_id\")", + "asc": true, + "isExpression": 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) OR (\"workspace_files\".\"context\" = 'project' AND \"workspace_files\".\"project_id\" IS NOT NULL))", + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -30674,6 +30876,169 @@ "policies": {}, "checkConstraints": {}, "isRLSEnabled": false + }, + "public.file_search_dependency": { + "name": "file_search_dependency", + "schema": "", + "columns": { + "build_id": { + "name": "build_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_content_updated_at": { + "name": "source_content_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "file_search_dependency_file_idx": { + "name": "file_search_dependency_file_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "build_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "file_search_dependency_build_id_workspace_file_search_build_id_fk": { + "name": "file_search_dependency_build_id_workspace_file_search_build_id_fk", + "tableFrom": "file_search_dependency", + "tableTo": "workspace_file_search_build", + "columnsFrom": ["build_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "file_search_dependency_build_id_file_id_pk": { + "name": "file_search_dependency_build_id_file_id_pk", + "columns": ["build_id", "file_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.file_search_dispatch_queue": { + "name": "file_search_dispatch_queue", + "schema": "", + "columns": { + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "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": { + "file_search_dispatch_schedule_idx": { + "name": "file_search_dispatch_schedule_idx", + "columns": [ + { + "expression": "last_dispatched_at", + "isExpression": false, + "asc": true, + "nulls": "first" + }, + { + "expression": "enqueued_at", + "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": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "file_search_dispatch_queue_entity_type_entity_id_pk": { + "name": "file_search_dispatch_queue_entity_type_entity_id_pk", + "columns": ["entity_type", "entity_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "file_search_dispatch_owner_check": { + "name": "file_search_dispatch_owner_check", + "value": "\"file_search_dispatch_queue\".\"entity_type\" IN ('workspace', 'project') AND length(\"file_search_dispatch_queue\".\"entity_id\") > 0" + } + }, + "isRLSEnabled": false } }, "enums": { diff --git a/packages/db/migrations/meta/0427_snapshot.json b/packages/db/migrations/meta/0427_snapshot.json index 166955bc856..d8d77570594 100644 --- a/packages/db/migrations/meta/0427_snapshot.json +++ b/packages/db/migrations/meta/0427_snapshot.json @@ -1,6 +1,6 @@ { - "id": "ec46a436-88d2-4e86-a5f5-87711de5b966", - "prevId": "6127dc99-6e8f-4842-9a28-7805522e406d", + "id": "ff76360a-4b7e-48b8-943f-7a9cc6980064", + "prevId": "ec46a436-88d2-4e86-a5f5-87711de5b966", "version": "7", "dialect": "postgresql", "tables": { @@ -29742,6 +29742,10 @@ "workspace_files_project_binding_check": { "name": "workspace_files_project_binding_check", "value": "(\"workspace_files\".\"project_id\" IS NOT NULL) = (\"workspace_files\".\"context\" = 'project') AND (\"workspace_files\".\"project_id\" IS NULL OR \"workspace_files\".\"chat_id\" IS NULL)" + }, + "workspace_files_workspace_binding_check": { + "name": "workspace_files_workspace_binding_check", + "value": "\"workspace_files\".\"context\" NOT IN ('workspace', 'chat', 'mothership', 'execution', 'workspace-logos') OR \"workspace_files\".\"workspace_id\" IS NOT NULL" } }, "isRLSEnabled": false diff --git a/packages/db/migrations/meta/0428_snapshot.json b/packages/db/migrations/meta/0428_snapshot.json deleted file mode 100644 index d8d77570594..00000000000 --- a/packages/db/migrations/meta/0428_snapshot.json +++ /dev/null @@ -1,31327 +0,0 @@ -{ - "id": "ff76360a-4b7e-48b8-943f-7a9cc6980064", - "prevId": "ec46a436-88d2-4e86-a5f5-87711de5b966", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.account": { - "name": "account", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "account_id": { - "name": "account_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "provider_id": { - "name": "provider_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "access_token": { - "name": "access_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "refresh_token": { - "name": "refresh_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "id_token": { - "name": "id_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "access_token_expires_at": { - "name": "access_token_expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "refresh_token_expires_at": { - "name": "refresh_token_expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "scope": { - "name": "scope", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "oauth_config": { - "name": "oauth_config", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "refresh_revoked_at": { - "name": "refresh_revoked_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "refresh_revoked_code": { - "name": "refresh_revoked_code", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "refresh_revoked_token_hash": { - "name": "refresh_revoked_token_hash", - "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.changelog_change": { - "name": "changelog_change", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "release_id": { - "name": "release_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "position": { - "name": "position", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "text": { - "name": "text", - "type": "text", - "primaryKey": false, - "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 - }, - "chat_id": { - "name": "chat_id", - "type": "uuid", - "primaryKey": false, - "notNull": false - } - }, - "indexes": { - "changelog_change_release_position_unique": { - "name": "changelog_change_release_position_unique", - "columns": [ - { - "expression": "release_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "position", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": { - "changelog_change_release_id_changelog_release_id_fk": { - "name": "changelog_change_release_id_changelog_release_id_fk", - "tableFrom": "changelog_change", - "tableTo": "changelog_release", - "columnsFrom": ["release_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "changelog_change_workflow_id_workflow_id_fk": { - "name": "changelog_change_workflow_id_workflow_id_fk", - "tableFrom": "changelog_change", - "tableTo": "workflow", - "columnsFrom": ["workflow_id"], - "columnsTo": ["id"], - "onDelete": "set null", - "onUpdate": "no action" - }, - "changelog_change_deployment_version_id_workflow_deployment_version_id_fk": { - "name": "changelog_change_deployment_version_id_workflow_deployment_version_id_fk", - "tableFrom": "changelog_change", - "tableTo": "workflow_deployment_version", - "columnsFrom": ["deployment_version_id"], - "columnsTo": ["id"], - "onDelete": "set null", - "onUpdate": "no action" - }, - "changelog_change_chat_id_copilot_chats_id_fk": { - "name": "changelog_change_chat_id_copilot_chats_id_fk", - "tableFrom": "changelog_change", - "tableTo": "copilot_chats", - "columnsFrom": ["chat_id"], - "columnsTo": ["id"], - "onDelete": "set null", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.changelog_release": { - "name": "changelog_release", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "workspace_id": { - "name": "workspace_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "version_major": { - "name": "version_major", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "version_minor": { - "name": "version_minor", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "version_patch": { - "name": "version_patch", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "bump_reason": { - "name": "bump_reason", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "body_file_id": { - "name": "body_file_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "revision": { - "name": "revision", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 1 - }, - "published_at": { - "name": "published_at", - "type": "timestamp (3)", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "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": { - "changelog_release_workspace_version_unique": { - "name": "changelog_release_workspace_version_unique", - "columns": [ - { - "expression": "workspace_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "version_major", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "version_minor", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "version_patch", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": false, - "method": "btree", - "with": {} - }, - "changelog_release_body_file_unique": { - "name": "changelog_release_body_file_unique", - "columns": [ - { - "expression": "body_file_id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": false, - "method": "btree", - "with": {} - }, - "changelog_release_workspace_published_idx": { - "name": "changelog_release_workspace_published_idx", - "columns": [ - { - "expression": "workspace_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "published_at", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": { - "changelog_release_workspace_id_workspace_id_fk": { - "name": "changelog_release_workspace_id_workspace_id_fk", - "tableFrom": "changelog_release", - "tableTo": "workspace", - "columnsFrom": ["workspace_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "changelog_release_body_file_id_workspace_files_id_fk": { - "name": "changelog_release_body_file_id_workspace_files_id_fk", - "tableFrom": "changelog_release", - "tableTo": "workspace_files", - "columnsFrom": ["body_file_id"], - "columnsTo": ["id"], - "onDelete": "no action", - "onUpdate": "no action" - }, - "changelog_release_created_by_user_id_fk": { - "name": "changelog_release_created_by_user_id_fk", - "tableFrom": "changelog_release", - "tableTo": "user", - "columnsFrom": ["created_by"], - "columnsTo": ["id"], - "onDelete": "set null", - "onUpdate": "no action" - }, - "changelog_release_updated_by_user_id_fk": { - "name": "changelog_release_updated_by_user_id_fk", - "tableFrom": "changelog_release", - "tableTo": "user", - "columnsFrom": ["updated_by"], - "columnsTo": ["id"], - "onDelete": "set null", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": { - "changelog_release_version_check": { - "name": "changelog_release_version_check", - "value": "\"changelog_release\".\"version_major\" >= 0 AND \"changelog_release\".\"version_minor\" >= 0 AND \"changelog_release\".\"version_patch\" >= 0" - } - }, - "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": true, - "default": 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 - }, - "memory_space_id": { - "name": "memory_space_id", - "type": "uuid", - "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 - }, - "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 - }, - "display_content": { - "name": "display_content", - "type": "jsonb", - "primaryKey": false, - "notNull": false - }, - "stream_id": { - "name": "stream_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "model": { - "name": "model", - "type": "text", - "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.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_uploaded_by_idx": { - "name": "doc_uploaded_by_idx", - "columns": [ - { - "expression": "uploaded_by", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "where": "\"document\".\"uploaded_by\" IS NOT NULL", - "concurrently": true, - "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_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_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": { - "fastupdate": "off" - } - } - }, - "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_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": {} - }, - "execution_large_values_workspace_id_idx": { - "name": "execution_large_values_workspace_id_idx", - "columns": [ - { - "expression": "workspace_id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "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": false - }, - "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 - }, - "project_id": { - "name": "project_id", - "type": "text", - "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": {} - }, - "folder_project_id_idx": { - "name": "folder_project_id_idx", - "columns": [ - { - "expression": "project_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_project_resource_parent_name_active_unique": { - "name": "folder_project_resource_parent_name_active_unique", - "columns": [ - { - "expression": "project_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 AND \"folder\".\"project_id\" IS NOT NULL", - "concurrently": true, - "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" - }, - "folder_project_id_project_id_fk": { - "name": "folder_project_id_project_id_fk", - "tableFrom": "folder", - "tableTo": "project", - "columnsFrom": ["project_id"], - "columnsTo": ["id"], - "onDelete": "restrict", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": { - "folder_owner_check": { - "name": "folder_owner_check", - "value": "num_nonnulls(\"folder\".\"workspace_id\", \"folder\".\"project_id\") = 1 AND (\"folder\".\"project_id\" IS NULL OR \"folder\".\"resource_type\" = 'file')" - } - }, - "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 - }, - "image_removal_pending": { - "name": "image_removal_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": {} - }, - "kcsl_terminal_started_at_idx": { - "name": "kcsl_terminal_started_at_idx", - "columns": [ - { - "expression": "started_at", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "where": "\"knowledge_connector_sync_log\".\"status\" IN ('completed', 'failed')", - "concurrently": true, - "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_benchmark_runs": { - "name": "mothership_benchmark_runs", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "benchmark_id": { - "name": "benchmark_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "label": { - "name": "label", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "evaluation_key": { - "name": "evaluation_key", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "reviews": { - "name": "reviews", - "type": "jsonb", - "primaryKey": false, - "notNull": true, - "default": "'[]'::jsonb" - }, - "version": { - "name": "version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 1 - }, - "reviewed_at": { - "name": "reviewed_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "correct": { - "name": "correct", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "automatic_correct": { - "name": "automatic_correct", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "total": { - "name": "total", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "artifacts": { - "name": "artifacts", - "type": "jsonb", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": { - "mothership_benchmark_runs_benchmark_created_idx": { - "name": "mothership_benchmark_runs_benchmark_created_idx", - "columns": [ - { - "expression": "benchmark_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": { - "mothership_benchmark_runs_benchmark_id_mothership_benchmarks_id_fk": { - "name": "mothership_benchmark_runs_benchmark_id_mothership_benchmarks_id_fk", - "tableFrom": "mothership_benchmark_runs", - "tableTo": "mothership_benchmarks", - "columnsFrom": ["benchmark_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": { - "mothership_benchmark_runs_score_check": { - "name": "mothership_benchmark_runs_score_check", - "value": "\"mothership_benchmark_runs\".\"total\" > 0 AND \"mothership_benchmark_runs\".\"correct\" >= 0 AND \"mothership_benchmark_runs\".\"correct\" <= \"mothership_benchmark_runs\".\"total\"" - }, - "mothership_benchmark_runs_version_check": { - "name": "mothership_benchmark_runs_version_check", - "value": "\"mothership_benchmark_runs\".\"version\" > 0" - }, - "mothership_benchmark_runs_automatic_score_check": { - "name": "mothership_benchmark_runs_automatic_score_check", - "value": "\"mothership_benchmark_runs\".\"automatic_correct\" >= 0 AND \"mothership_benchmark_runs\".\"automatic_correct\" <= \"mothership_benchmark_runs\".\"total\"" - } - }, - "isRLSEnabled": false - }, - "public.mothership_benchmarks": { - "name": "mothership_benchmarks", - "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 - }, - "run_as_user_id": { - "name": "run_as_user_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "source_workspace_id": { - "name": "source_workspace_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "artifacts": { - "name": "artifacts", - "type": "jsonb", - "primaryKey": false, - "notNull": true - }, - "version": { - "name": "version", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 1 - }, - "running_stage": { - "name": "running_stage", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "attempt_id": { - "name": "attempt_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "lease_expires_at": { - "name": "lease_expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "planner_chat_id": { - "name": "planner_chat_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()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": { - "mothership_benchmarks_owner_created_idx": { - "name": "mothership_benchmarks_owner_created_idx", - "columns": [ - { - "expression": "organization_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "user_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "created_at", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - }, - "mothership_benchmarks_workspace_idx": { - "name": "mothership_benchmarks_workspace_idx", - "columns": [ - { - "expression": "source_workspace_id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": { - "mothership_benchmarks_organization_id_organization_id_fk": { - "name": "mothership_benchmarks_organization_id_organization_id_fk", - "tableFrom": "mothership_benchmarks", - "tableTo": "organization", - "columnsFrom": ["organization_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mothership_benchmarks_user_id_user_id_fk": { - "name": "mothership_benchmarks_user_id_user_id_fk", - "tableFrom": "mothership_benchmarks", - "tableTo": "user", - "columnsFrom": ["user_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mothership_benchmarks_run_as_user_id_user_id_fk": { - "name": "mothership_benchmarks_run_as_user_id_user_id_fk", - "tableFrom": "mothership_benchmarks", - "tableTo": "user", - "columnsFrom": ["run_as_user_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mothership_benchmarks_source_workspace_id_workspace_id_fk": { - "name": "mothership_benchmarks_source_workspace_id_workspace_id_fk", - "tableFrom": "mothership_benchmarks", - "tableTo": "workspace", - "columnsFrom": ["source_workspace_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": { - "mothership_benchmarks_version_check": { - "name": "mothership_benchmarks_version_check", - "value": "\"mothership_benchmarks\".\"version\" > 0" - }, - "mothership_benchmarks_attempt_check": { - "name": "mothership_benchmarks_attempt_check", - "value": "(\"mothership_benchmarks\".\"running_stage\" IS NULL AND \"mothership_benchmarks\".\"attempt_id\" IS NULL AND \"mothership_benchmarks\".\"lease_expires_at\" IS NULL) OR (\"mothership_benchmarks\".\"running_stage\" IS NOT NULL AND \"mothership_benchmarks\".\"running_stage\" IN ('distill', 'redact', 'plan', 'reconstruct', 'grade') AND \"mothership_benchmarks\".\"attempt_id\" IS NOT NULL AND \"mothership_benchmarks\".\"lease_expires_at\" IS NOT NULL)" - } - }, - "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_memory_selections": { - "name": "mothership_memory_selections", - "schema": "", - "columns": { - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "organization_id": { - "name": "organization_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "space_id": { - "name": "space_id", - "type": "uuid", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "mothership_memory_selections_user_id_user_id_fk": { - "name": "mothership_memory_selections_user_id_user_id_fk", - "tableFrom": "mothership_memory_selections", - "tableTo": "user", - "columnsFrom": ["user_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mothership_memory_selections_organization_id_organization_id_fk": { - "name": "mothership_memory_selections_organization_id_organization_id_fk", - "tableFrom": "mothership_memory_selections", - "tableTo": "organization", - "columnsFrom": ["organization_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mothership_memory_selections_space_id_mothership_memory_spaces_id_fk": { - "name": "mothership_memory_selections_space_id_mothership_memory_spaces_id_fk", - "tableFrom": "mothership_memory_selections", - "tableTo": "mothership_memory_spaces", - "columnsFrom": ["space_id"], - "columnsTo": ["id"], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "mothership_memory_selections_user_id_organization_id_pk": { - "name": "mothership_memory_selections_user_id_organization_id_pk", - "columns": ["user_id", "organization_id"] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.mothership_memory_spaces": { - "name": "mothership_memory_spaces", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "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 - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": { - "mothership_memory_spaces_owner_idx": { - "name": "mothership_memory_spaces_owner_idx", - "columns": [ - { - "expression": "user_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "organization_id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": { - "mothership_memory_spaces_user_id_user_id_fk": { - "name": "mothership_memory_spaces_user_id_user_id_fk", - "tableFrom": "mothership_memory_spaces", - "tableTo": "user", - "columnsFrom": ["user_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mothership_memory_spaces_organization_id_organization_id_fk": { - "name": "mothership_memory_spaces_organization_id_organization_id_fk", - "tableFrom": "mothership_memory_spaces", - "tableTo": "organization", - "columnsFrom": ["organization_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "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.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_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 - }, - "organization_scope_key": { - "name": "organization_scope_key", - "type": "text", - "primaryKey": false, - "notNull": false, - "generated": { - "as": "CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END", - "type": "stored" - } - }, - "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": { - "project_id_organization_scope_unique": { - "name": "project_id_organization_scope_unique", - "nullsNotDistinct": false, - "columns": ["id", "organization_scope_key"] - } - }, - "policies": {}, - "checkConstraints": { - "project_name_length": { - "name": "project_name_length", - "value": "char_length(btrim(\"project\".\"name\")) BETWEEN 1 AND 100" - } - }, - "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": false - }, - "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()" - }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "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": {} - }, - "public_share_entity_id_idx": { - "name": "public_share_entity_id_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "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": { - "public_share_entity_binding_check": { - "name": "public_share_entity_binding_check", - "value": "\n (\"public_share\".\"entity_type\" IS NULL AND \"public_share\".\"entity_id\" IS NULL AND \"public_share\".\"workspace_id\" IS NOT NULL)\n OR (\"public_share\".\"entity_type\" IS NOT NULL AND \"public_share\".\"entity_id\" IS NOT NULL AND char_length(\"public_share\".\"entity_id\") > 0\n AND ((\"public_share\".\"entity_type\" = 'workspace' AND \"public_share\".\"workspace_id\" IS NOT NULL AND \"public_share\".\"entity_id\" = \"public_share\".\"workspace_id\")\n OR (\"public_share\".\"entity_type\" = 'project' AND \"public_share\".\"workspace_id\" IS NULL AND \"public_share\".\"resource_type\" = 'file')))\n " - } - }, - "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 - }, - "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_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_created_by_idx": { - "name": "user_table_def_created_by_idx", - "columns": [ - { - "expression": "created_by", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "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_created_by_idx": { - "name": "user_table_rows_created_by_idx", - "columns": [ - { - "expression": "created_by", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "where": "\"user_table_rows\".\"created_by\" IS NOT NULL", - "concurrently": true, - "method": "btree", - "with": {} - }, - "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": { - "fastupdate": "off" - } - }, - "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_level_idx": { - "name": "workflow_execution_logs_workspace_activity_level_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" - }, - { - "expression": "level", - "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 - }, - "organization_scope_key": { - "name": "organization_scope_key", - "type": "text", - "primaryKey": false, - "notNull": false, - "generated": { - "as": "CASE WHEN organization_id IS NULL THEN 'personal' ELSE 'organization:' || organization_id END", - "type": "stored" - } - }, - "project_id": { - "name": "project_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "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_project_id_id_idx": { - "name": "workspace_project_id_id_idx", - "columns": [ - { - "expression": "project_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "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_project_id_project_id_fk": { - "name": "workspace_project_id_project_id_fk", - "tableFrom": "workspace", - "tableTo": "project", - "columnsFrom": ["project_id"], - "columnsTo": ["id"], - "onDelete": "restrict", - "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" - }, - "workspace_project_organization_fk": { - "name": "workspace_project_organization_fk", - "tableFrom": "workspace", - "tableTo": "project", - "columnsFrom": ["project_id", "organization_scope_key"], - "columnsTo": ["id", "organization_scope_key"], - "onDelete": "no action", - "onUpdate": "no action" - }, - "workspace_fork_project_fk": { - "name": "workspace_fork_project_fk", - "tableFrom": "workspace", - "tableTo": "workspace", - "columnsFrom": ["forked_from_workspace_id", "project_id"], - "columnsTo": ["id", "project_id"], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "workspace_id_project_unique": { - "name": "workspace_id_project_unique", - "nullsNotDistinct": false, - "columns": ["id", "project_id"] - } - }, - "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()" - }, - "after_entity_type": { - "name": "after_entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "after_entity_id": { - "name": "after_entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "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": false - }, - "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 - }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "artifact_key": { - "name": "artifact_key", - "type": "text", - "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": { - "file_search_build_owner_check": { - "name": "file_search_build_owner_check", - "value": "(\"workspace_file_search_build\".\"entity_type\" IS NULL AND \"workspace_file_search_build\".\"entity_id\" IS NULL AND \"workspace_file_search_build\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_build\".\"entity_type\" = 'workspace' AND \"workspace_file_search_build\".\"entity_id\" = \"workspace_file_search_build\".\"workspace_id\") OR (\"workspace_file_search_build\".\"entity_type\" = 'project' AND length(\"workspace_file_search_build\".\"entity_id\") > 0 AND \"workspace_file_search_build\".\"workspace_id\" IS NULL)" - } - }, - "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": false - }, - "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 - }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "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" - } - }, - "file_search_chunk_owner_content_idx": { - "name": "file_search_chunk_owner_content_idx", - "columns": [ - { - "expression": "entity_type", - "isExpression": false, - "asc": true, - "nulls": "last", - "opclass": "text_ops" - }, - { - "expression": "entity_id", - "isExpression": false, - "asc": true, - "nulls": "last", - "opclass": "text_ops" - }, - { - "expression": "content", - "isExpression": false, - "asc": true, - "nulls": "last", - "opclass": "gin_trgm_ops" - } - ], - "isUnique": false, - "where": "\"workspace_file_search_chunk\".\"entity_type\" = 'project'", - "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" - }, - "file_search_chunk_owner_check": { - "name": "file_search_chunk_owner_check", - "value": "(\"workspace_file_search_chunk\".\"entity_type\" IS NULL AND \"workspace_file_search_chunk\".\"entity_id\" IS NULL AND \"workspace_file_search_chunk\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_chunk\".\"entity_type\" = 'workspace' AND \"workspace_file_search_chunk\".\"entity_id\" = \"workspace_file_search_chunk\".\"workspace_id\") OR (\"workspace_file_search_chunk\".\"entity_type\" = 'project' AND length(\"workspace_file_search_chunk\".\"entity_id\") > 0 AND \"workspace_file_search_chunk\".\"workspace_id\" IS NULL)" - } - }, - "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": false - }, - "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()" - }, - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "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": {} - }, - "file_search_revision_owner_pending_idx": { - "name": "file_search_revision_owner_pending_idx", - "columns": [ - { - "expression": "coalesce(\"entity_type\", 'workspace')", - "asc": true, - "isExpression": true, - "nulls": "last" - }, - { - "expression": "coalesce(\"entity_id\", \"workspace_id\")", - "asc": true, - "isExpression": 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": true, - "method": "btree", - "with": {} - }, - "file_search_revision_owner_status_idx": { - "name": "file_search_revision_owner_status_idx", - "columns": [ - { - "expression": "coalesce(\"entity_type\", 'workspace')", - "asc": true, - "isExpression": true, - "nulls": "last" - }, - { - "expression": "coalesce(\"entity_id\", \"workspace_id\")", - "asc": true, - "isExpression": true, - "nulls": "last" - }, - { - "expression": "status", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "dispatched_at", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "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": { - "file_search_revision_owner_check": { - "name": "file_search_revision_owner_check", - "value": "(\"workspace_file_search_revision\".\"entity_type\" IS NULL AND \"workspace_file_search_revision\".\"entity_id\" IS NULL AND \"workspace_file_search_revision\".\"workspace_id\" IS NOT NULL) OR (\"workspace_file_search_revision\".\"entity_type\" = 'workspace' AND \"workspace_file_search_revision\".\"entity_id\" = \"workspace_file_search_revision\".\"workspace_id\") OR (\"workspace_file_search_revision\".\"entity_type\" = 'project' AND length(\"workspace_file_search_revision\".\"entity_id\") > 0 AND \"workspace_file_search_revision\".\"workspace_id\" IS NULL)" - } - }, - "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": false - }, - "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 - }, - "project_id": { - "name": "project_id", - "type": "text", - "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_workspace_display_name_idx": { - "name": "workspace_files_workspace_display_name_idx", - "columns": [ - { - "expression": "workspace_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "regexp_replace(regexp_replace(regexp_replace(normalize(\"original_name\", NFC), '^[ \\t\\n\\v\\f\\r\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]+|[ \\t\\n\\v\\f\\r\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]+$', '', 'g'), '[\\x01-\\x1f\\x7f]', '', 'g'), '[ \\t\\n\\v\\f\\r\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]+', ' ', 'g')", - "asc": true, - "isExpression": 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": {} - }, - "workspace_files_project_id_idx": { - "name": "workspace_files_project_id_idx", - "columns": [ - { - "expression": "project_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": true, - "method": "btree", - "with": {} - }, - "workspace_files_project_folder_name_active_unique": { - "name": "workspace_files_project_folder_name_active_unique", - "columns": [ - { - "expression": "project_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\".\"project_id\" IS NOT NULL", - "concurrently": true, - "method": "btree", - "with": {} - }, - "workspace_files_search_owner_keyset_idx": { - "name": "workspace_files_search_owner_keyset_idx", - "columns": [ - { - "expression": "(CASE WHEN \"project_id\" IS NOT NULL THEN 'project' ELSE 'workspace' END)", - "asc": true, - "isExpression": true, - "nulls": "last" - }, - { - "expression": "coalesce(\"project_id\", \"workspace_id\")", - "asc": true, - "isExpression": 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) OR (\"workspace_files\".\"context\" = 'project' AND \"workspace_files\".\"project_id\" IS NOT NULL))", - "concurrently": true, - "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" - }, - "workspace_files_project_id_project_id_fk": { - "name": "workspace_files_project_id_project_id_fk", - "tableFrom": "workspace_files", - "tableTo": "project", - "columnsFrom": ["project_id"], - "columnsTo": ["id"], - "onDelete": "restrict", - "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)" - }, - "workspace_files_owner_check": { - "name": "workspace_files_owner_check", - "value": "num_nonnulls(\"workspace_files\".\"workspace_id\", \"workspace_files\".\"project_id\", \"workspace_files\".\"organization_id\") <= 1" - }, - "workspace_files_project_binding_check": { - "name": "workspace_files_project_binding_check", - "value": "(\"workspace_files\".\"project_id\" IS NOT NULL) = (\"workspace_files\".\"context\" = 'project') AND (\"workspace_files\".\"project_id\" IS NULL OR \"workspace_files\".\"chat_id\" IS NULL)" - }, - "workspace_files_workspace_binding_check": { - "name": "workspace_files_workspace_binding_check", - "value": "\"workspace_files\".\"context\" NOT IN ('workspace', 'chat', 'mothership', 'execution', 'workspace-logos') OR \"workspace_files\".\"workspace_id\" IS NOT 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 - }, - "public.file_search_dependency": { - "name": "file_search_dependency", - "schema": "", - "columns": { - "build_id": { - "name": "build_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "file_id": { - "name": "file_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "key": { - "name": "key", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "source_content_updated_at": { - "name": "source_content_updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - } - }, - "indexes": { - "file_search_dependency_file_idx": { - "name": "file_search_dependency_file_idx", - "columns": [ - { - "expression": "file_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "build_id", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": { - "file_search_dependency_build_id_workspace_file_search_build_id_fk": { - "name": "file_search_dependency_build_id_workspace_file_search_build_id_fk", - "tableFrom": "file_search_dependency", - "tableTo": "workspace_file_search_build", - "columnsFrom": ["build_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "file_search_dependency_build_id_file_id_pk": { - "name": "file_search_dependency_build_id_file_id_pk", - "columns": ["build_id", "file_id"] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.file_search_dispatch_queue": { - "name": "file_search_dispatch_queue", - "schema": "", - "columns": { - "entity_type": { - "name": "entity_type", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "entity_id": { - "name": "entity_id", - "type": "text", - "primaryKey": false, - "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": { - "file_search_dispatch_schedule_idx": { - "name": "file_search_dispatch_schedule_idx", - "columns": [ - { - "expression": "last_dispatched_at", - "isExpression": false, - "asc": true, - "nulls": "first" - }, - { - "expression": "enqueued_at", - "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": false, - "concurrently": false, - "method": "btree", - "with": {} - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": { - "file_search_dispatch_queue_entity_type_entity_id_pk": { - "name": "file_search_dispatch_queue_entity_type_entity_id_pk", - "columns": ["entity_type", "entity_id"] - } - }, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": { - "file_search_dispatch_owner_check": { - "name": "file_search_dispatch_owner_check", - "value": "\"file_search_dispatch_queue\".\"entity_type\" IN ('workspace', 'project') AND length(\"file_search_dispatch_queue\".\"entity_id\") > 0" - } - }, - "isRLSEnabled": false - } - }, - "enums": { - "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", - "project_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 bb5bc57f9e3..43131fbacd9 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -2966,36 +2966,29 @@ { "idx": 424, "version": "7", - "when": 1791934984607, - "tag": "0424_file_creator_lifetime", - "breakpoints": true - }, - { - "idx": 425, - "version": "7", "when": 1791934984608, - "tag": "0425_project_file_upload_sessions", + "tag": "0424_project_file_upload_sessions", "breakpoints": true }, { - "idx": 426, + "idx": 425, "version": "7", "when": 1791934984609, - "tag": "0426_public_share_entity_ownership", + "tag": "0425_public_share_entity_ownership", "breakpoints": true }, { - "idx": 427, + "idx": 426, "version": "7", "when": 1791934984610, - "tag": "0427_file_search_owner_scope", + "tag": "0426_file_search_owner_scope", "breakpoints": true }, { - "idx": 428, + "idx": 427, "version": "7", "when": 1791934984611, - "tag": "0428_file_workspace_binding", + "tag": "0427_file_workspace_binding", "breakpoints": true } ] diff --git a/packages/db/public-share-ownership.integration.ts b/packages/db/public-share-ownership.integration.ts index a425678237f..1a9c0bdc029 100644 --- a/packages/db/public-share-ownership.integration.ts +++ b/packages/db/public-share-ownership.integration.ts @@ -19,8 +19,7 @@ const migrations = [ '0358_workspace_file_content_version_precision.sql', '0422_file_entity_ownership.sql', '0423_file_folder_version_ownership.sql', - '0424_file_creator_lifetime.sql', - '0426_public_share_entity_ownership.sql', + '0425_public_share_entity_ownership.sql', ].map((name) => readFileSync(new URL(`./migrations/${name}`, import.meta.url), 'utf8')) const checks: { name: string; status: 'passed' | 'failed'; durationMs: number; error?: string }[] = [] diff --git a/packages/db/scripts/file-ownership-reconciliation.integration.ts b/packages/db/scripts/file-ownership-reconciliation.integration.ts index f2676f3f4b7..3becd688a50 100644 --- a/packages/db/scripts/file-ownership-reconciliation.integration.ts +++ b/packages/db/scripts/file-ownership-reconciliation.integration.ts @@ -39,7 +39,7 @@ describe('file ownership provisioning and interrupted migration recovery', () => await writeFile( join(directory, '0001_upload_purpose.sql'), await readFile( - new URL('../migrations/0425_project_file_upload_sessions.sql', import.meta.url) + new URL('../migrations/0424_project_file_upload_sessions.sql', import.meta.url) ) ) await writeFile( diff --git a/packages/db/scripts/reconcile-file-ownership.ts b/packages/db/scripts/reconcile-file-ownership.ts index 01447efdaeb..27d43725121 100644 --- a/packages/db/scripts/reconcile-file-ownership.ts +++ b/packages/db/scripts/reconcile-file-ownership.ts @@ -13,9 +13,8 @@ const migrations = [ '0359_workspace_file_search_chunks.sql', '0422_file_entity_ownership.sql', '0423_file_folder_version_ownership.sql', - '0424_file_creator_lifetime.sql', - '0426_public_share_entity_ownership.sql', - '0427_file_search_owner_scope.sql', + '0425_public_share_entity_ownership.sql', + '0426_file_search_owner_scope.sql', ] try { From 41a3b55d876e01680b6c8412d5dc90833d0740ee Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 13 Oct 2026 19:11:46 -0700 Subject: [PATCH 107/113] test(files): narrow recorded database insert values --- .../handlers/agent/agent-handler.test.ts | 13 ++- .../rate-limiter/provider-admission.test.ts | 3 +- .../document-processing-source.test.ts | 3 +- .../documents/storage-cleanup.test.ts | 5 +- .../sim/lib/memory/message-provenance.test.ts | 8 +- .../file-round-trip-provenance.test.ts | 12 +- apps/sim/lib/secrets/usage/record.test.ts | 7 +- .../execution/execution-file-manager.test.ts | 7 +- .../uploads/upload-session/service.test.ts | 9 +- scripts/check-test-types.baseline.json | 104 +++--------------- 10 files changed, 59 insertions(+), 112 deletions(-) diff --git a/apps/sim/executor/handlers/agent/agent-handler.test.ts b/apps/sim/executor/handlers/agent/agent-handler.test.ts index 1ec5a5d29c8..bc063dcd541 100644 --- a/apps/sim/executor/handlers/agent/agent-handler.test.ts +++ b/apps/sim/executor/handlers/agent/agent-handler.test.ts @@ -18,6 +18,7 @@ import { workspaceFileSecretProvenanceMock, workspaceFileSecretProvenanceMockFns, } from '@sim/testing/mocks/workspace-file-secret-provenance.mock' +import { toArray, toRecord } from '@sim/utils/object' import { afterAll, afterEach, @@ -604,7 +605,7 @@ describe('AgentBlockHandler', () => { const original = structuredClone(inputs) await handler.execute({ ...mockContext, executionId: 'exec-1' }, mockBlock, inputs) const stored = dbChainMockFns.values.mock.calls - .map(([row]) => row) + .map(([row]) => toRecord(row)) .find((row) => Array.isArray(row.data) && row.data[0]?.role === 'user')?.data as Message[] expect(stored).toBeDefined() expect(stored[0].files).toEqual([ @@ -672,9 +673,11 @@ describe('AgentBlockHandler', () => { files: [file], }) expect(mockExecuteProviderRequest.mock.calls[0][1].messages[0].files).toHaveLength(1) - expect(dbChainMockFns.values.mock.calls.some(([row]) => row.data?.[0]?.role === 'user')).toBe( - false - ) + expect( + dbChainMockFns.values.mock.calls.some( + ([row]) => toRecord(toArray(toRecord(row).data)[0]).role === 'user' + ) + ).toBe(false) }) }) @@ -842,7 +845,7 @@ describe('AgentBlockHandler', () => { } const memoryWrites = dbChainMockFns.values.mock.calls - .map(([row]) => row) + .map(([row]) => toRecord(row)) .filter((row) => Array.isArray(row.data)) expect(memoryWrites.map((row) => row.data)).toEqual([ [currentUserMessage], diff --git a/apps/sim/lib/core/rate-limiter/provider-admission.test.ts b/apps/sim/lib/core/rate-limiter/provider-admission.test.ts index 8bd52a15c51..c31c01567da 100644 --- a/apps/sim/lib/core/rate-limiter/provider-admission.test.ts +++ b/apps/sim/lib/core/rate-limiter/provider-admission.test.ts @@ -1,3 +1,4 @@ +import type { rateLimitBucket } from '@sim/db/schema' import { dbChainMockFns, resetDbChainMock } from '@sim/testing' import { resetEnvMock, setEnv } from '@sim/testing/mocks/env.mock' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -168,7 +169,7 @@ describe('provider admission', () => { } let stored: { key: string; tokens: string; lastRefillAt: Date } | undefined dbChainMockFns.values.mockImplementation((row) => { - stored ??= { ...row } + stored ??= { ...(row as typeof rateLimitBucket.$inferSelect) } return { onConflictDoNothing: vi.fn().mockResolvedValue(undefined) } }) dbChainMockFns.limit.mockImplementation(async () => [stored]) diff --git a/apps/sim/lib/knowledge/documents/document-processing-source.test.ts b/apps/sim/lib/knowledge/documents/document-processing-source.test.ts index 11b71f0414b..ef3df0d1a39 100644 --- a/apps/sim/lib/knowledge/documents/document-processing-source.test.ts +++ b/apps/sim/lib/knowledge/documents/document-processing-source.test.ts @@ -22,6 +22,7 @@ import { workspaceFileSecretProvenanceMock, workspaceFileSecretProvenanceMockFns, } from '@sim/testing/mocks/workspace-file-secret-provenance.mock' +import { toRecord } from '@sim/utils/object' import { tasks } from '@trigger.dev/sdk' import { DrizzleQueryError } from 'drizzle-orm/errors' import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -1149,7 +1150,7 @@ describe('in-process quota continuation dispatch', () => { }) ) - const deferredUntil = dbChainMockFns.values.mock.calls[0][0].availableAt as Date + const deferredUntil = toRecord(dbChainMockFns.values.mock.calls[0][0]).availableAt as Date expect(deferredUntil.getTime()).toBeGreaterThanOrEqual(1_000 + 5 * 60 * 1000 * 0.8) expect(deferredUntil.getTime()).toBeLessThanOrEqual(1_000 + 5 * 60 * 1000 * 1.2) diff --git a/apps/sim/lib/knowledge/documents/storage-cleanup.test.ts b/apps/sim/lib/knowledge/documents/storage-cleanup.test.ts index 5807345fab6..8d87fccd686 100644 --- a/apps/sim/lib/knowledge/documents/storage-cleanup.test.ts +++ b/apps/sim/lib/knowledge/documents/storage-cleanup.test.ts @@ -5,6 +5,7 @@ import { uploadsMetadataMock, uploadsMetadataMockFns, } from '@sim/testing/mocks/uploads-metadata.mock' +import { toArray } from '@sim/utils/object' import { beforeEach, describe, expect, it, vi } from 'vitest' import type { OutboxEventContext } from '@/lib/core/outbox/service' @@ -94,7 +95,9 @@ describe('durable knowledge storage cleanup', () => { 'request-1' ) expect(mockGetBindings.mock.calls.map(([keys]) => keys.length)).toEqual([100, 100, 51]) - expect(dbChainMockFns.values.mock.calls.map(([rows]) => rows.length)).toEqual([100, 100, 51]) + expect(dbChainMockFns.values.mock.calls.map(([rows]) => toArray(rows).length)).toEqual([ + 100, 100, 51, + ]) }) it('propagates persistence failures before the parent transaction can commit', async () => { diff --git a/apps/sim/lib/memory/message-provenance.test.ts b/apps/sim/lib/memory/message-provenance.test.ts index 67db443a13a..a9f17ce77b5 100644 --- a/apps/sim/lib/memory/message-provenance.test.ts +++ b/apps/sim/lib/memory/message-provenance.test.ts @@ -10,7 +10,8 @@ import { workspaceContextMock, workspaceContextMockFns, } from '@sim/testing/mocks/workspace-context.mock' -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { toArray, toRecord } from '@sim/utils/object' +import { assert, beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('@/lib/core/security/encryption', () => encryptionMock) vi.mock('@/lib/logs/execution/pii-redaction', () => piiRedactionMock) @@ -160,7 +161,7 @@ describe('memory message provenance', () => { }) const sidecar = dbChainMockFns.values.mock.calls - .map(([value]) => value) + .map(([value]) => toRecord(value)) .find((value) => value.memoryId === 'memory-1') expect(sidecar).toMatchObject({ status: 'exact', @@ -169,7 +170,8 @@ describe('memory message provenance', () => { expect(dbChainMockFns.execute.mock.invocationCallOrder[0]).toBeLessThan( dbChainMockFns.select.mock.invocationCallOrder[0] ) - queueStoredMemory(data, sidecar.entries) + assert.isDefined(sidecar) + queueStoredMemory(data, toArray(sidecar.entries)) const result = await new Memory().fetchMemoryMessages(executionContext(), INPUTS) expect(result[0].content).toBe('{{TOKEN}}') expect(loggedErrors()).toEqual([]) diff --git a/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts b/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts index bb248c8b12b..607fb400d3c 100644 --- a/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts +++ b/apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts @@ -5,6 +5,7 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { Readable } from 'node:stream' import { promisify } from 'node:util' +import type { StoredWorkspaceFileSecretProvenanceEntry } from '@sim/db/schema' import { copilotChats, workspace, workspaceFiles } from '@sim/db/schema' import { dbChainMockFns, queueTableRows, resetDbChainMock } from '@sim/testing' import { authBanMock } from '@sim/testing/mocks/auth-ban.mock' @@ -28,7 +29,8 @@ import { workspaceFileFoldersMock, workspaceFileFoldersMockFns, } from '@sim/testing/mocks/workspace-file-folders.mock' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { toArray, toRecord } from '@sim/utils/object' +import { afterEach, assert, beforeEach, describe, expect, it, vi } from 'vitest' const hoisted = vi.hoisted(() => ({ find: vi.fn(), @@ -310,12 +312,16 @@ beforeEach(async () => { }) expect(result.created).toBe(true) const sidecar = dbChainMockFns.values.mock.calls - .map(([value]) => value) + .map(([value]) => toRecord(value)) .find((value) => value.fileId === PUBLISHED.id && 'entries' in value) expect(sidecar).toMatchObject({ contentUpdatedAt: REVISION }) + assert.isDefined(sidecar) storedProvenance = sidecar.status === 'exact' - ? { status: 'exact', entries: sidecar.entries } + ? { + status: 'exact', + entries: toArray(sidecar.entries), + } : { status: 'unknown' } registered = true queueTableRows(workspaceFiles, [PUBLISHED]) diff --git a/apps/sim/lib/secrets/usage/record.test.ts b/apps/sim/lib/secrets/usage/record.test.ts index 11c041f1651..fe34449e7b9 100644 --- a/apps/sim/lib/secrets/usage/record.test.ts +++ b/apps/sim/lib/secrets/usage/record.test.ts @@ -1,5 +1,6 @@ import { dbChainMockFns, resetDbChainMock } from '@sim/testing' import { flushMacrotask } from '@sim/testing/helpers/async' +import { toArray } from '@sim/utils/object' import { beforeEach, describe, expect, it, vi } from 'vitest' import { recordSecretUsage } from '@/lib/secrets/usage/record' @@ -28,7 +29,7 @@ describe('recordSecretUsage', () => { await flushMacrotask() expect(dbChainMockFns.insert).toHaveBeenCalledTimes(1) - const rows = dbChainMockFns.values.mock.calls[0]?.[0] + const rows = toArray(dbChainMockFns.values.mock.calls[0]?.[0]) expect(rows).toHaveLength(2) expect(rows[0]).toMatchObject({ workspaceId: 'workspace-1', @@ -71,7 +72,9 @@ describe('recordSecretUsage', () => { } await flushMacrotask() - expect(dbChainMockFns.values.mock.calls[0]?.[0][0]).toMatchObject({ usageDate: '2026-03-14' }) + expect(toArray(dbChainMockFns.values.mock.calls[0]?.[0])[0]).toMatchObject({ + usageDate: '2026-03-14', + }) }) it('increments the existing bucket instead of inserting a duplicate', async () => { diff --git a/apps/sim/lib/uploads/contexts/execution/execution-file-manager.test.ts b/apps/sim/lib/uploads/contexts/execution/execution-file-manager.test.ts index aa3454e027d..40d4555e607 100644 --- a/apps/sim/lib/uploads/contexts/execution/execution-file-manager.test.ts +++ b/apps/sim/lib/uploads/contexts/execution/execution-file-manager.test.ts @@ -4,6 +4,7 @@ import { uploadsConfigMock, uploadsConfigMockFns, } from '@sim/testing/mocks/uploads-config.mock' +import { toRecord } from '@sim/utils/object' import { beforeEach, describe, expect, it, vi } from 'vitest' const { mockUploadToS3, mockGetPresignedUrlWithConfig, mockDeleteFromS3 } = vi.hoisted(() => ({ @@ -72,7 +73,7 @@ describe('uploadExecutionFile key allocation', () => { const scope = { ...context, workspaceId: '11111111-1111-4111-8111-111111111111' } let persisted: Record | undefined dbChainMockFns.returning.mockImplementation(async () => { - persisted = dbChainMockFns.values.mock.calls.at(-1)?.[0] + persisted = toRecord(dbChainMockFns.values.mock.calls.at(-1)?.[0]) return [{ ...persisted }] }) const uploaded = await uploadExecutionFile( @@ -100,7 +101,7 @@ describe('uploadExecutionFile key allocation', () => { it('commits tracked provenance with the canonical file before returning its URL', async () => { const contentUpdatedAt = new Date('2026-01-01T00:00:00Z') dbChainMockFns.returning.mockImplementation(async () => { - const values = dbChainMockFns.values.mock.calls.at(-1)?.[0] + const values = toRecord(dbChainMockFns.values.mock.calls.at(-1)?.[0]) return [{ ...values, id: values?.id ?? values?.fileId, contentUpdatedAt }] }) const file = await uploadExecutionFile( @@ -178,7 +179,7 @@ describe('uploadExecutionFile key allocation', () => { it('cleans both committed metadata and bytes when its download URL cannot be issued', async () => { const contentUpdatedAt = new Date('2026-01-01T00:00:00Z') dbChainMockFns.returning.mockImplementation(async () => { - const values = dbChainMockFns.values.mock.calls.at(-1)?.[0] + const values = toRecord(dbChainMockFns.values.mock.calls.at(-1)?.[0]) return [{ ...values, id: values?.id ?? values?.fileId, contentUpdatedAt }] }) mockGetPresignedUrlWithConfig.mockRejectedValueOnce(new Error('Signing failed')) diff --git a/apps/sim/lib/uploads/upload-session/service.test.ts b/apps/sim/lib/uploads/upload-session/service.test.ts index a69bfd25da2..983c2bd67b5 100644 --- a/apps/sim/lib/uploads/upload-session/service.test.ts +++ b/apps/sim/lib/uploads/upload-session/service.test.ts @@ -12,6 +12,7 @@ import { workspaceUploadsMock, workspaceUploadsMockFns, } from '@sim/testing/mocks/workspace-uploads.mock' +import { toRecord } from '@sim/utils/object' import { eq, inArray, isNull } from 'drizzle-orm' import { beforeEach, describe, expect, it, vi } from 'vitest' import { createCopilotChatPrincipal } from '@/lib/mothership/auth/application-delegation' @@ -147,11 +148,11 @@ describe('upload sessions', () => { expect(mockCreatePutTransfer).toHaveBeenCalledWith( expect.objectContaining({ key: FINAL_KEY, uploadId: 'upload-1' }) ) - const inserted = dbChainMockFns.values.mock.calls[0][0] + const inserted = toRecord(dbChainMockFns.values.mock.calls[0][0]) expect(inserted.tokenHash).toBe(sha256Hex(created.uploadToken)) expect(inserted.tokenHash).not.toBe(created.uploadToken) expect(inserted.finalKey).toBe(FINAL_KEY) - expect(inserted.metadata.authBinding).toEqual({ + expect(toRecord(inserted.metadata).authBinding).toEqual({ version: 1, workspaceId: WORKSPACE_ID, principal: createSessionPrincipal(), @@ -300,7 +301,7 @@ describe('upload sessions', () => { }, }, }) - const metadata = dbChainMockFns.values.mock.calls[0][0].metadata + const metadata = toRecord(toRecord(dbChainMockFns.values.mock.calls[0][0]).metadata) expect(metadata.folderId).toBe('folder') expect(Object.hasOwn(metadata, WORKSPACE_FILE_UPLOAD_PROVENANCE_KEY)).toBe(false) }) @@ -352,7 +353,7 @@ describe('upload sessions', () => { ...extra, } as Parameters[0]) - const { finalKey } = dbChainMockFns.values.mock.calls[0][0] + const { finalKey } = dbChainMockFns.values.mock.calls[0][0] as UploadSessionRecord const lastComponent = finalKey.slice(finalKey.lastIndexOf('/') + 1) expect( Buffer.byteLength(`${lastComponent}${LOCAL_UPLOAD_METADATA_SUFFIX}`, 'utf-8') diff --git a/scripts/check-test-types.baseline.json b/scripts/check-test-types.baseline.json index b54a1d20dfb..ff919c61449 100644 --- a/scripts/check-test-types.baseline.json +++ b/scripts/check-test-types.baseline.json @@ -194,9 +194,6 @@ "apps/sim/app/api/knowledge/slack/oauth/callback/route.test.ts": { "TS2554": 8 }, - "apps/sim/app/api/knowledge/utils.test.ts": { - "TS2493": 1 - }, "apps/sim/app/api/link-preview/route.test.ts": { "TS2554": 1 }, @@ -428,8 +425,6 @@ "TS2322": 1 }, "apps/sim/app/api/webhooks/route.test.ts": { - "TS2352": 1, - "TS2493": 1, "TS2554": 15 }, "apps/sim/app/api/webhooks/slack/route.test.ts": { @@ -750,9 +745,6 @@ "TS2345": 1, "TS2740": 17 }, - "apps/sim/ee/access-requests/lib/notifications.test.ts": { - "TS2493": 2 - }, "apps/sim/ee/scim/lib/identity/resolve-user.test.ts": { "TS18046": 2 }, @@ -760,9 +752,7 @@ "TS2345": 2 }, "apps/sim/ee/scim/lib/projection/reconcile-user.test.ts": { - "TS2345": 15, - "TS2352": 1, - "TS2493": 1 + "TS2345": 15 }, "apps/sim/ee/scim/lib/reconcile/job.test.ts": { "TS2345": 6 @@ -780,10 +770,6 @@ "TS2345": 1, "TS2554": 1 }, - "apps/sim/ee/workspace-forking/lib/copy/copy-resources.test.ts": { - "TS2352": 1, - "TS2493": 3 - }, "apps/sim/ee/workspace-forking/lib/copy/copy-workflows.test.ts": { "TS2322": 1, "TS2345": 1, @@ -834,11 +820,9 @@ }, "apps/sim/executor/handlers/agent/agent-handler.test.ts": { "TS18046": 2, - "TS18048": 6, - "TS2339": 4, + "TS2339": 3, "TS2345": 1, "TS2352": 1, - "TS2493": 3, "TS7006": 1 }, "apps/sim/executor/handlers/agent/memory.test.ts": { @@ -1027,9 +1011,6 @@ "apps/sim/lib/auth/oauth-provider-metadata.test.ts": { "TS2345": 2 }, - "apps/sim/lib/auth/oauth-token-family.test.ts": { - "TS2493": 1 - }, "apps/sim/lib/auth/principal.test.ts": { "TS2345": 3 }, @@ -1172,8 +1153,7 @@ "TS2352": 1 }, "apps/sim/lib/core/rate-limiter/provider-admission.test.ts": { - "TS2345": 1, - "TS7006": 1 + "TS2345": 1 }, "apps/sim/lib/core/rate-limiter/rate-limiter.test.ts": { "TS2307": 4, @@ -1268,10 +1248,6 @@ "TS18048": 2, "TS2493": 2 }, - "apps/sim/lib/credentials/environment.test.ts": { - "TS2339": 1, - "TS2493": 1 - }, "apps/sim/lib/credentials/orchestration/index.test.ts": { "TS2352": 2, "TS2493": 2 @@ -1335,7 +1311,7 @@ }, "apps/sim/lib/execution/payloads/large-value-metadata.test.ts": { "TS2339": 2, - "TS2493": 3 + "TS2493": 1 }, "apps/sim/lib/execution/payloads/prune-metadata-sql.test.ts": { "TS2345": 1, @@ -1459,10 +1435,6 @@ "TS2345": 1, "TS2741": 2 }, - "apps/sim/lib/internal/file/operations.provenance.test.ts": { - "TS2352": 1, - "TS2493": 1 - }, "apps/sim/lib/internal/file/operations.test.ts": { "TS2345": 1 }, @@ -1719,7 +1691,6 @@ }, "apps/sim/lib/knowledge/connectors/sync-persistence.test.ts": { "TS2345": 2, - "TS2352": 1, "TS2769": 1 }, "apps/sim/lib/knowledge/documents/document-indexing-usage.test.ts": { @@ -1727,10 +1698,7 @@ "TS2739": 2 }, "apps/sim/lib/knowledge/documents/document-processing-source.test.ts": { - "TS2345": 4, - "TS2352": 1, - "TS2493": 2, - "TS2532": 1 + "TS2345": 4 }, "apps/sim/lib/knowledge/documents/processing-lane.test.ts": { "TS2322": 1 @@ -1760,14 +1728,11 @@ }, "apps/sim/lib/knowledge/documents/storage-cleanup.test.ts": { "TS18046": 1, - "TS18048": 1, - "TS2345": 1, - "TS2493": 1 + "TS2345": 1 }, "apps/sim/lib/knowledge/documents/workspace-source-provenance.test.ts": { "TS2339": 1, - "TS2345": 4, - "TS2493": 1 + "TS2345": 4 }, "apps/sim/lib/knowledge/mcp/route-handler.test.ts": { "TS2345": 1 @@ -1887,8 +1852,6 @@ "TS2741": 4 }, "apps/sim/lib/memory/message-provenance.test.ts": { - "TS18048": 2, - "TS2493": 1, "TS4104": 1 }, "apps/sim/lib/memory/replay-provenance.test.ts": { @@ -1922,9 +1885,7 @@ "TS2339": 1 }, "apps/sim/lib/mothership/agent-cli/file-round-trip-provenance.test.ts": { - "TS18048": 4, - "TS2345": 1, - "TS2493": 1 + "TS2345": 1 }, "apps/sim/lib/mothership/agent-cli/file-upload-origin.test.ts": { "TS2345": 1 @@ -1951,8 +1912,7 @@ }, "apps/sim/lib/mothership/chat/messages-store.test.ts": { "TS18048": 7, - "TS2352": 1, - "TS2493": 2 + "TS2493": 1 }, "apps/sim/lib/mothership/chat/payload.test.ts": { "TS2322": 3, @@ -2060,12 +2020,6 @@ "apps/sim/lib/mothership/tools/handlers/workflow/withheld-run-result.test.ts": { "TS2322": 1 }, - "apps/sim/lib/mothership/tools/server/files/doc-compile.test.ts": { - "TS2322": 3 - }, - "apps/sim/lib/mothership/tools/server/files/doc-servable.test.ts": { - "TS2322": 2 - }, "apps/sim/lib/mothership/tools/server/knowledge/workspace-search.test.ts": { "TS18046": 1 }, @@ -2114,10 +2068,8 @@ "TS2341": 1 }, "apps/sim/lib/secrets/usage/record.test.ts": { - "TS18048": 2, "TS2339": 2, - "TS2493": 3, - "TS2532": 1 + "TS2493": 1 }, "apps/sim/lib/selectors/manifest.test.ts": { "TS2339": 3 @@ -2188,10 +2140,6 @@ "apps/sim/lib/table/delete-runner.test.ts": { "TS2502": 1 }, - "apps/sim/lib/table/dispatch-governed-subject.test.ts": { - "TS2352": 1, - "TS2493": 1 - }, "apps/sim/lib/table/dispatcher.test.ts": { "TS2322": 1 }, @@ -2211,8 +2159,8 @@ "TS2322": 10 }, "apps/sim/lib/table/rows/executions.test.ts": { - "TS2352": 2, - "TS2493": 4 + "TS2352": 1, + "TS2493": 3 }, "apps/sim/lib/table/rows/secret-provenance.test.ts": { "TS2493": 4 @@ -2241,8 +2189,7 @@ "TS2741": 2 }, "apps/sim/lib/table/views/service.test.ts": { - "TS2322": 5, - "TS2352": 1 + "TS2322": 5 }, "apps/sim/lib/table/workflow-columns.test.ts": { "TS2741": 4 @@ -2268,11 +2215,6 @@ "apps/sim/lib/uploads/client/upload-session.test.ts": { "TS2322": 7 }, - "apps/sim/lib/uploads/contexts/execution/execution-file-manager.test.ts": { - "TS2339": 4, - "TS2493": 3, - "TS2698": 3 - }, "apps/sim/lib/uploads/contexts/organization-logo/application.test.ts": { "TS2352": 1 }, @@ -2295,11 +2237,8 @@ "TS2322": 1 }, "apps/sim/lib/uploads/upload-session/service.test.ts": { - "TS18048": 4, - "TS2339": 2, - "TS2345": 1, - "TS2493": 3, - "TS2532": 1 + "TS2339": 1, + "TS2345": 1 }, "apps/sim/lib/uploads/utils/fetch-external-url.server.test.ts": { "TS2345": 3 @@ -2451,7 +2390,6 @@ "TS2352": 1 }, "apps/sim/lib/workflows/executor/human-in-the-loop-manager.test.ts": { - "TS2493": 2, "TS2554": 2 }, "apps/sim/lib/workflows/migrations/subblock-migrations.test.ts": { @@ -2465,18 +2403,9 @@ "TS2339": 1, "TS2352": 1 }, - "apps/sim/lib/workflows/persistence/deployment-operations.test.ts": { - "TS2493": 1 - }, - "apps/sim/lib/workflows/persistence/duplicate.test.ts": { - "TS2493": 1 - }, "apps/sim/lib/workflows/persistence/persist-block-access-gate.test.ts": { "TS2345": 2 }, - "apps/sim/lib/workflows/persistence/utils.test.ts": { - "TS2493": 2 - }, "apps/sim/lib/workflows/references/dependent-reconfigs.test.ts": { "TS2322": 1 }, @@ -2565,9 +2494,6 @@ "TS2493": 1, "TS2550": 1 }, - "apps/sim/lib/workspaces/admin-move.test.ts": { - "TS2493": 1 - }, "apps/sim/lib/workspaces/application/manage-permissions.test.ts": { "TS2345": 1 }, From a6d8ab98f9ca6f4f1a36e8c2882ce0c388b23aad Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 13 Oct 2026 21:26:14 -0700 Subject: [PATCH 108/113] fix(files): preserve owned content and generated search delivery --- .../file-versions.integration.ts | 77 ++++++++++++------- .../workspace/workspace-file-manager.ts | 66 ++++++---------- apps/sim/lib/uploads/documents/compile.ts | 17 ++-- .../lib/uploads/documents/compiled-store.ts | 10 +-- .../application/admit-file-content.ts | 2 +- .../fetch-servable-workspace-file-buffer.ts | 9 ++- .../search-workspace-file-content.test.ts | 25 ++++-- .../search-workspace-file-content.ts | 17 +++- .../search/dispatcher.integration.ts | 4 +- .../search/owners.integration.ts | 70 ++++++++++++++--- .../0426_file_search_owner_scope.sql | 14 ++-- 11 files changed, 197 insertions(+), 114 deletions(-) diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts index 2a5e92737ee..1cc36f7b5a5 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-versions.integration.ts @@ -10,6 +10,7 @@ import { outboxEvent, permissions, user, + workflowTest, workspace, workspaceFileSecretProvenance, workspaceFiles, @@ -43,6 +44,7 @@ import { createAgentCliTransport } from '@/lib/mothership/agent-cli/transport' import { handoffFileCreatorsInTx } from '@/lib/uploads/contexts/workspace/creator-handoff' import { WORKSPACE_FILE_STORAGE_CLEANUP_OUTBOX_EVENT } from '@/lib/uploads/contexts/workspace/file-outbox-events' import { + createOwnedWorkspaceFile, deleteWorkspaceFileVersion, fetchWorkspaceFileBuffer, getWorkspaceFile, @@ -64,13 +66,7 @@ import { releaseWorkspaceFileVersionsForPurgeInTx, } from '@/lib/uploads/contexts/workspace/workspace-file-versions' import * as storageService from '@/lib/uploads/core/storage-service' -import { v2FileErrorPolicies } from '@/lib/workspace-files/api' -import { presentWorkspaceFileText } from '@/lib/workspace-files/api/text-presenter' -import { - downloadWorkspaceFileVersion, - readWorkspaceFileVersionText, - revertWorkspaceFileVersion, -} from '@/lib/workspace-files/application/file-versions' +import { revertWorkspaceFileVersion } from '@/lib/workspace-files/application/file-versions' import { revokeWorkspaceAccessTx } from '@/lib/workspaces/access/workspace-access' import { runCleanupFileVersions } from '@/background/cleanup-file-versions' import { runCleanupSoftDeletes } from '@/background/cleanup-soft-deletes' @@ -355,6 +351,7 @@ describe('workspace file version history in PostgreSQL', () => { ) it.each([ + { operation: 'owned', enqueueAvailable: true, code: 'CONNECTION_CLOSED' }, { operation: 'upload', enqueueAvailable: true, code: undefined }, { operation: 'content', enqueueAvailable: true, code: undefined }, { operation: 'upload', enqueueAvailable: false, code: undefined }, @@ -389,23 +386,41 @@ describe('workspace file version history in PostgreSQL', () => { }) try { const result = - operation === 'content' - ? updateWorkspaceFileContent( - fixture.workspaceId, - fixture.fileId, - fixture.aliceId, - Buffer.from('committed content'), - undefined, - { version: { source: 'api', authorUserId: fixture.aliceId } } - ) - : uploadWorkspaceFile( - fixture.workspaceId, - fixture.aliceId, - Buffer.from('committed content'), - 'committed.txt', - 'text/plain', - { notifyWorkspaceChange: false } - ) + operation === 'owned' + ? createOwnedWorkspaceFile({ + context: 'test', + workspaceId: fixture.workspaceId, + userId: fixture.aliceId, + content: 'committed content', + contentType: 'text/plain', + fileName: (id) => `${id}.txt`, + insertOwner: async (tx, bodyFileId) => + tx.insert(workflowTest).values({ + id: generateId(), + workspaceId: fixture.workspaceId, + name: generateId(), + title: 'Commit outcome', + bodyFileId, + sourceHash: 'fixture', + }), + }) + : operation === 'content' + ? updateWorkspaceFileContent( + fixture.workspaceId, + fixture.fileId, + fixture.aliceId, + Buffer.from('committed content'), + undefined, + { version: { source: 'api', authorUserId: fixture.aliceId } } + ) + : uploadWorkspaceFile( + fixture.workspaceId, + fixture.aliceId, + Buffer.from('committed content'), + 'committed.txt', + 'text/plain', + { notifyWorkspaceChange: false } + ) await expect(result).rejects.toThrow('Commit acknowledgement lost') } finally { capture.mockRestore() @@ -417,11 +432,15 @@ describe('workspace file version history in PostgreSQL', () => { .from(workspaceFiles) .where(eq(workspaceFiles.key, stagedKey)) expect(committed?.id).toEqual(expect.any(String)) - const file = await getWorkspaceFile(fixture.workspaceId, committed.id) - if (!file) throw new Error('committed file missing') - expect((await fetchWorkspaceFileBuffer(file, { maxBytes: 1024 })).toString()).toBe( - 'committed content' - ) + expect( + ( + await storageService.downloadFile({ + key: stagedKey, + context: 'workspace', + maxBytes: 1024, + }) + ).toString() + ).toBe('committed content') expect( await db .select({ id: outboxEvent.id }) 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 a34a6f9c590..3da0fabf374 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -789,54 +789,36 @@ export async function createOwnedWorkspaceFile(params: { 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, + const staged = await stageFileContent({ + owner: { entityType: 'workspace', entityId: params.workspaceId }, + userId: params.userId, + name, contentType: params.contentType, - context: 'workspace', - preserveKey: true, - customKey: storageKey, - metadata: { - originalName: name, - uploadedAt: new Date().toISOString(), - purpose: 'workspace', + content: buffer, + }) + const { owner, updatedUsage } = await finalizeStagedFileContent(staged, async (tx) => { + const inserted = await insertWorkspaceFileMetadataInTx(tx, { + id: fileId, + key: staged.key, 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 } + folderId: null, + originalName: name, + contentType: params.contentType, + size: buffer.length, + context: params.context, }) - void maybeNotifyStorageLimitForBillingContext(storageBillingContext, updatedUsage) - return { fileId, owner } - } catch (error) { - await cleanupWorkspaceStorageObject( - uploadResult.key, - `${params.context} file owner insert failure` + if (!inserted) throw new Error(`Owned ${params.context} file ${fileId} was not inserted`) + const updatedUsage = await incrementStorageUsageForBillingContextInTx( + tx, + storageBillingContext, + buffer.length ) - throw error - } + return { owner: await params.insertOwner(tx, fileId), updatedUsage } + }) + void maybeNotifyStorageLimitForBillingContext(storageBillingContext, updatedUsage) + return { fileId, owner } } /** diff --git a/apps/sim/lib/uploads/documents/compile.ts b/apps/sim/lib/uploads/documents/compile.ts index 9ff53153fc9..22ffa7f37e3 100644 --- a/apps/sim/lib/uploads/documents/compile.ts +++ b/apps/sim/lib/uploads/documents/compile.ts @@ -29,7 +29,10 @@ import { iterateDocumentFileReferences, } from '@/lib/uploads/documents/references' import { getFileExtension, getMimeTypeFromExtension } from '@/lib/uploads/utils/file-utils' -import { admitWorkspaceFiles } from '@/lib/workspace-files/application/admit-file-content' +import { + admitWorkspaceFiles, + workspaceFileProvenanceIdentity, +} from '@/lib/workspace-files/application/admit-file-content' import { readWorkspaceFileContent } from '@/lib/workspace-files/application/read-workspace-file-content' import { readWorkspaceFileMetadata } from '@/lib/workspace-files/application/read-workspace-file-metadata' import type { SandboxTaskId } from '@/sandbox-tasks/registry' @@ -193,12 +196,12 @@ function touchesReferencedFiles( function referencedImageIdentities( resolution: ReferencedImageResolution ): WorkspaceFileSecretProvenanceIdentity[] { - return resolution.images.map(({ record }) => ({ - fileId: record.id, - key: record.key, - context: record.storageContext ?? 'workspace', - contentUpdatedAt: record.contentUpdatedAt ?? record.updatedAt, - })) + return resolution.images.map(({ record }) => + workspaceFileProvenanceIdentity({ + ...record, + contentUpdatedAt: record.contentUpdatedAt ?? record.updatedAt, + }) + ) } /** Collects static input identities, retaining one over the cap so callers can reject oversized sets. */ diff --git a/apps/sim/lib/uploads/documents/compiled-store.ts b/apps/sim/lib/uploads/documents/compiled-store.ts index 352e8e60cf0..94267aa3b84 100644 --- a/apps/sim/lib/uploads/documents/compiled-store.ts +++ b/apps/sim/lib/uploads/documents/compiled-store.ts @@ -164,8 +164,7 @@ export async function publishCompiledDocArtifact( owner: string | EditableFileOwner, source: string, ext: string, - referencedInputIdentity: string, - onArtifactWrite?: (key: string) => void + referencedInputIdentity: string ): Promise { if (!referencedInputIdentity) { throw new Error('Published compiled document identity must not be empty') @@ -175,7 +174,6 @@ export async function publishCompiledDocArtifact( if (existing?.referencedInputIdentity === referencedInputIdentity) return const pointer: PublishedArtifactPointer = { version: 1, referencedInputIdentity } try { - onArtifactWrite?.(key) await uploadFile({ file: Buffer.from(JSON.stringify(pointer), 'utf-8'), fileName: `doc.${ext}.published.json`, @@ -285,12 +283,10 @@ export async function storeCompiledDoc( ext: string, contentType: string, binary: Buffer, - referencedInputIdentity?: string, - onArtifactWrite?: (key: string) => void + referencedInputIdentity?: string ): Promise { const key = compiledArtifactKey(owner, source, ext, referencedInputIdentity) try { - onArtifactWrite?.(key) await uploadFile({ file: binary, fileName: `doc.${ext}`, @@ -300,7 +296,7 @@ export async function storeCompiledDoc( preserveKey: true, }) if (referencedInputIdentity) { - await publishCompiledDocArtifact(owner, source, ext, referencedInputIdentity, onArtifactWrite) + await publishCompiledDocArtifact(owner, source, ext, referencedInputIdentity) } } catch (err) { logger.error('Failed to store compiled doc artifact', { diff --git a/apps/sim/lib/workspace-files/application/admit-file-content.ts b/apps/sim/lib/workspace-files/application/admit-file-content.ts index 3e8a8898fa0..8f0d1de4658 100644 --- a/apps/sim/lib/workspace-files/application/admit-file-content.ts +++ b/apps/sim/lib/workspace-files/application/admit-file-content.ts @@ -113,7 +113,7 @@ export async function requireAdmittedContributors( * Admits each of many files on its own, classifying all of them in one batch, and returns the ids * admitted. A caller no gate observes gets every id back without a provenance read. */ -export async function admitWorkspaceFileIdentities( +async function admitWorkspaceFileIdentities( principal: Principal, workspaceId: string, identities: readonly WorkspaceFileSecretProvenanceIdentity[], diff --git a/apps/sim/lib/workspace-files/application/fetch-servable-workspace-file-buffer.ts b/apps/sim/lib/workspace-files/application/fetch-servable-workspace-file-buffer.ts index ddff42a5d80..32ca93373c4 100644 --- a/apps/sim/lib/workspace-files/application/fetch-servable-workspace-file-buffer.ts +++ b/apps/sim/lib/workspace-files/application/fetch-servable-workspace-file-buffer.ts @@ -39,7 +39,12 @@ export async function fetchAuthorizedServableWorkspaceFileBuffer( } ) const artifactKey = result.artifactKey - if (artifactKey && result.contributingFiles?.every((file) => file.contentUpdatedAt) !== false) { + if ( + artifactKey && + result.contributingFiles?.every( + (file) => file.contentUpdatedAt && ['workspace', 'test', 'changelog'].includes(file.context) + ) !== false + ) { await db.transaction((tx) => markFileSearchArtifactReadyInTx(tx, { owner: { entityType: 'workspace', entityId: fileRecord.workspaceId }, @@ -49,7 +54,7 @@ export async function fetchAuthorizedServableWorkspaceFileBuffer( contentUpdatedAt: fileRecord.contentUpdatedAt ?? fileRecord.updatedAt, }, dependencies: (result.contributingFiles ?? []).map((file) => { - if (!file.contentUpdatedAt || file.context !== 'workspace') + if (!file.contentUpdatedAt) throw new Error('Artifact input has no canonical workspace revision') return { fileId: file.fileId, diff --git a/apps/sim/lib/workspace-files/application/search-workspace-file-content.test.ts b/apps/sim/lib/workspace-files/application/search-workspace-file-content.test.ts index 52c22bf423d..bc0e59b9b16 100644 --- a/apps/sim/lib/workspace-files/application/search-workspace-file-content.test.ts +++ b/apps/sim/lib/workspace-files/application/search-workspace-file-content.test.ts @@ -83,11 +83,13 @@ describe('searchWorkspaceFileContent cancellation', () => { }) ) await expect( - searchWorkspaceFileContent.execute({ - principal, - input, - request: { headers: new Headers() }, - }) + observeContentDelivery(cleanOnlyGate, () => + searchWorkspaceFileContent.execute({ + principal: copilotPrincipal(), + input, + request: { headers: new Headers() }, + }) + ) ).rejects.toMatchObject({ code: 'locked' }) }) }) @@ -215,7 +217,18 @@ describe('searchWorkspaceFileContent for Chat', () => { expect(found.results[0].text).toMatch(/preview withheld/i) }) - it("leaves a person's search limit and result untouched", async () => { + it("preserves a person's point-in-time results after matching rows change", async () => { + resetDbChainMock() + queueTableRows(schemaMock.workspace, [ + { + workspaceId: 'workspace-1', + workspaceOrganizationId: null, + allowPersonalApiKeys: true, + billedAccountUserId: 'user-1', + }, + ]) + queueTableRows(schemaMock.workspaceFiles, []) + queueTableRows(schemaMock.workspaceFileSearchRevision, []) const raw = { results: [hit('refused', 1)], count: 1, diff --git a/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts b/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts index 462aa6d15e8..f2378a365c4 100644 --- a/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts +++ b/apps/sim/lib/workspace-files/application/search-workspace-file-content.ts @@ -86,6 +86,22 @@ export const searchWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ folderScope, signal, }) + if (!classified) { + const ownedInputs = new Map( + result.sources.flatMap((source) => + source.dependencies + .filter((dependency) => dependency.context !== 'workspace') + .map((dependency) => [dependency.fileId, dependency] as const) + ) + ) + for (const dependency of ownedInputs.values()) + await assertOwnedFileAccess(principal, { + ...context, + fileId: dependency.fileId, + fileContext: dependency.context ?? '', + }) + return toWorkspaceFileSearchResult(result) + } const provenance = await db.transaction(async (tx) => { await lockWorkspaceProject(tx, context.workspaceId) const [current] = await tx @@ -125,7 +141,6 @@ export const searchWorkspaceFileContent = defineAuthorizedWorkspaceFileUseCase({ view: 'excerpt' as const, })) ) - if (!classified) return toWorkspaceFileSearchResult(result) const admittedIds = new Set(admitted.map((source) => source.identity.fileId)) return toWorkspaceFileSearchResult({ ...result, diff --git a/apps/sim/lib/workspace-files/search/dispatcher.integration.ts b/apps/sim/lib/workspace-files/search/dispatcher.integration.ts index b820bd615ba..fc059f1f76c 100644 --- a/apps/sim/lib/workspace-files/search/dispatcher.integration.ts +++ b/apps/sim/lib/workspace-files/search/dispatcher.integration.ts @@ -119,8 +119,10 @@ describe('workspace file search dispatch PostgreSQL deadlines', () => { await connection`CREATE TABLE workspace_files ( id text PRIMARY KEY, workspace_id text, context text NOT NULL, deleted_at timestamp, content_updated_at timestamp NOT NULL, - project_id text, key text NOT NULL DEFAULT 'key' + project_id text, organization_id text, chat_id text, key text NOT NULL DEFAULT 'key' )` + await connection`CREATE TABLE workflow_test (body_file_id text, workspace_id text, deleted_at timestamp)` + await connection`CREATE TABLE changelog_release (body_file_id text, workspace_id text)` await connection`CREATE TABLE workspace_file_search_revision ( file_id text PRIMARY KEY, workspace_id text NOT NULL, source_content_updated_at timestamp NOT NULL, status text NOT NULL DEFAULT 'pending', diff --git a/apps/sim/lib/workspace-files/search/owners.integration.ts b/apps/sim/lib/workspace-files/search/owners.integration.ts index abcd573e646..ab2915b1af3 100644 --- a/apps/sim/lib/workspace-files/search/owners.integration.ts +++ b/apps/sim/lib/workspace-files/search/owners.integration.ts @@ -25,6 +25,7 @@ import { createWorkspaceApiKeyPrincipal, } from '@sim/testing/factories/principal.factory' import { createDeferred } from '@sim/testing/helpers/deferred' +import { envFlagsMock, setEnvFlags } from '@sim/testing/mocks/env-flags.mock' import { featureFlagsMock, featureFlagsMockFns } from '@sim/testing/mocks/feature-flags.mock' import { setUploadDirServer, uploadsSetupMock } from '@sim/testing/mocks/uploads-setup.mock' import { getErrorMessage } from '@sim/utils/errors' @@ -36,6 +37,7 @@ import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('@/lib/uploads/core/setup.server', () => uploadsSetupMock) vi.mock('@/lib/core/config/feature-flags', () => featureFlagsMock) +vi.mock('@/lib/core/config/env-flags', () => envFlagsMock) import { getFileContentProvenance } from '@/lib/internal/file/operations' import { lockProject } from '@/lib/projects/membership' @@ -44,8 +46,9 @@ import { createOwnedWorkspaceFile, uploadWorkspaceFile, } from '@/lib/uploads/contexts/workspace/workspace-file-manager' -import { storeCompiledDoc } from '@/lib/uploads/documents/compiled-store' +import { compiledArtifactKey, storeCompiledDoc } from '@/lib/uploads/documents/compiled-store' import { fileDocumentInputIdentity } from '@/lib/uploads/documents/input-identity' +import { fetchAuthorizedServableWorkspaceFileBuffer } from '@/lib/workspace-files/application/fetch-servable-workspace-file-buffer' import { searchWorkspaceFileContent } from '@/lib/workspace-files/application/search-workspace-file-content' import { resolveFileSearchFolderScope } from '@/lib/workspace-files/search/delivery' import { prepareWorkspaceFileSearchDispatch } from '@/lib/workspace-files/search/dispatcher' @@ -315,6 +318,12 @@ describe('owner-scoped indexed file search on PostgreSQL and stored bytes', () = ], }) ) + setEnvFlags({ isDocSandboxEnabled: true }) + const served = await fetchAuthorizedServableWorkspaceFileBuffer(generated, f.principal, { + maxBytes: 1024 * 1024, + }) + expect(served.buffer).toEqual(artifact) + expect(served.contributingFiles?.map((file) => file.context)).toEqual([context]) await indexWorkspaceFileForSearch({ owner, ...(await claim(generated.id)) }, signal) const read = () => searchWorkspaceFileContent.execute({ @@ -345,6 +354,41 @@ describe('owner-scoped indexed file search on PostgreSQL and stored bytes', () = featureFlagsMockFns.mockIsFeatureEnabled.mockResolvedValue(false) await expect(read()).rejects.toMatchObject({ code: 'forbidden' }) featureFlagsMockFns.mockIsFeatureEnabled.mockResolvedValue(true) + await db + .delete(fileSearchDispatchQueue) + .where( + and( + eq(fileSearchDispatchQueue.entityType, 'workspace'), + eq(fileSearchDispatchQueue.entityId, f.workspaceId) + ) + ) + await db + .update(workspaceFiles) + .set({ contentUpdatedAt: new Date(inputRow.contentUpdatedAt.getTime() + 1000) }) + .where(eq(workspaceFiles.id, body.fileId)) + expect((await read()).results).toEqual([]) + expect( + await db + .select() + .from(fileSearchDispatchQueue) + .where( + and( + eq(fileSearchDispatchQueue.entityType, 'workspace'), + eq(fileSearchDispatchQueue.entityId, f.workspaceId) + ) + ) + ).toHaveLength(1) + expect( + await db + .select() + .from(workspaceFileSearchRevision) + .where(eq(workspaceFileSearchRevision.fileId, body.fileId)) + ).toEqual([]) + await db + .update(workspaceFiles) + .set({ contentUpdatedAt: inputRow.contentUpdatedAt }) + .where(eq(workspaceFiles.id, body.fileId)) + expect((await read()).count).toBe(1) if (context === 'test') await db.delete(workflowTest).where(eq(workflowTest.bodyFileId, body.fileId)) else await db.delete(changelogRelease).where(eq(changelogRelease.bodyFileId, body.fileId)) @@ -811,7 +855,6 @@ describe('owner-scoped indexed file search on PostgreSQL and stored bytes', () = .select() .from(workspaceFiles) .where(eq(workspaceFiles.id, dependency.file.id)) - const artifactKeys: string[] = [] await storeCompiledDoc( f.owner, source, @@ -820,11 +863,14 @@ describe('owner-scoped indexed file search on PostgreSQL and stored bytes', () = await Packer.toBuffer( new Document({ sections: [{ children: [new Paragraph('oldrender')] }] }) ), - fileDocumentInputIdentity(f.owner, [dependencyRow]), - (key) => artifactKeys.push(key) + fileDocumentInputIdentity(f.owner, [dependencyRow]) + ) + const artifactKey = compiledArtifactKey( + f.owner, + source, + 'docx', + fileDocumentInputIdentity(f.owner, [dependencyRow]) ) - const artifactKey = artifactKeys.find((key) => key.endsWith('.docx')) - if (!artifactKey) throw new Error('Artifact was not stored') const completion = { owner: f.owner, file: { @@ -884,7 +930,6 @@ describe('owner-scoped indexed file search on PostgreSQL and stored bytes', () = .select() .from(workspaceFiles) .where(eq(workspaceFiles.id, dependency.file.id)) - const artifactKeys: string[] = [] await storeCompiledDoc( f.owner, source, @@ -893,11 +938,14 @@ describe('owner-scoped indexed file search on PostgreSQL and stored bytes', () = await Packer.toBuffer( new Document({ sections: [{ children: [new Paragraph('old-contributor')] }] }) ), - fileDocumentInputIdentity(f.owner, [dependencyRow]), - (key) => artifactKeys.push(key) + fileDocumentInputIdentity(f.owner, [dependencyRow]) + ) + const artifactKey = compiledArtifactKey( + f.owner, + source, + 'docx', + fileDocumentInputIdentity(f.owner, [dependencyRow]) ) - const artifactKey = artifactKeys.find((key) => key.endsWith('.docx')) - if (!artifactKey) throw new Error('Artifact was not stored') const { markFileSearchArtifactReadyInTx } = await import( '@/lib/workspace-files/search/artifact-ready' ) diff --git a/packages/db/migrations/0426_file_search_owner_scope.sql b/packages/db/migrations/0426_file_search_owner_scope.sql index 8859a9eaf24..ad5944c09a1 100644 --- a/packages/db/migrations/0426_file_search_owner_scope.sql +++ b/packages/db/migrations/0426_file_search_owner_scope.sql @@ -116,9 +116,9 @@ BEGIN IF TG_OP = 'DELETE' THEN UPDATE workspace_file_search_build SET expires_at = now() WHERE id = (SELECT build_id FROM workspace_file_search_revision WHERE file_id = OLD.id); - owner_type := CASE WHEN OLD.project_id IS NOT NULL THEN 'project' WHEN OLD.context = 'workspace' THEN 'workspace' END; + owner_type := CASE WHEN OLD.project_id IS NOT NULL THEN 'project' WHEN OLD.context IN ('workspace', 'test', 'changelog') THEN 'workspace' END; owner_id := coalesce(OLD.project_id, OLD.workspace_id); - IF owner_type IN ('workspace', 'project') AND OLD.context = owner_type AND owner_id IS NOT NULL THEN + IF owner_type IN ('workspace', 'project') AND (OLD.context = owner_type OR (owner_type = 'workspace' AND OLD.context IN ('test', 'changelog'))) AND owner_id IS NOT NULL THEN INSERT INTO file_search_dispatch_queue (entity_type, entity_id) VALUES (owner_type, owner_id) ON CONFLICT (entity_type, entity_id) DO UPDATE SET updated_at = now(); END IF; @@ -134,21 +134,21 @@ BEGIN DELETE FROM workspace_file_search_revision WHERE file_id = NEW.id; IF ROW(NEW.context, NEW.workspace_id, NEW.project_id) IS DISTINCT FROM ROW(OLD.context, OLD.workspace_id, OLD.project_id) THEN - owner_type := CASE WHEN OLD.project_id IS NOT NULL THEN 'project' WHEN OLD.context = 'workspace' THEN 'workspace' END; + owner_type := CASE WHEN OLD.project_id IS NOT NULL THEN 'project' WHEN OLD.context IN ('workspace', 'test', 'changelog') THEN 'workspace' END; owner_id := coalesce(OLD.project_id, OLD.workspace_id); - IF owner_type IN ('workspace', 'project') AND OLD.context = owner_type AND owner_id IS NOT NULL THEN + IF owner_type IN ('workspace', 'project') AND (OLD.context = owner_type OR (owner_type = 'workspace' AND OLD.context IN ('test', 'changelog'))) AND owner_id IS NOT NULL THEN INSERT INTO file_search_dispatch_queue (entity_type, entity_id) VALUES (owner_type, owner_id) ON CONFLICT (entity_type, entity_id) DO UPDATE SET updated_at = now(); END IF; END IF; END IF; - owner_type := CASE WHEN NEW.project_id IS NOT NULL THEN 'project' WHEN NEW.context = 'workspace' THEN 'workspace' END; + owner_type := CASE WHEN NEW.project_id IS NOT NULL THEN 'project' WHEN NEW.context IN ('workspace', 'test', 'changelog') THEN 'workspace' END; owner_id := coalesce(NEW.project_id, NEW.workspace_id); - IF owner_type IN ('workspace', 'project') AND NEW.context = owner_type AND owner_id IS NOT NULL THEN + IF owner_type IN ('workspace', 'project') AND (NEW.context = owner_type OR (owner_type = 'workspace' AND NEW.context IN ('test', 'changelog'))) AND owner_id IS NOT NULL THEN -- Waking the owner also reconciles bounded dependent builds without rewriting their sources here. INSERT INTO file_search_dispatch_queue (entity_type, entity_id) VALUES (owner_type, owner_id) ON CONFLICT (entity_type, entity_id) DO UPDATE SET updated_at = now(); - IF NEW.deleted_at IS NULL THEN + IF NEW.deleted_at IS NULL AND NEW.context = owner_type THEN INSERT INTO workspace_file_search_revision (file_id, workspace_id, entity_type, entity_id, source_content_updated_at) VALUES (NEW.id, NEW.workspace_id, owner_type, owner_id, NEW.content_updated_at) ON CONFLICT (file_id) DO NOTHING; From 1faa8db3f09467023e21d8042ef007bfb00cc45e Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 13 Oct 2026 22:45:20 -0700 Subject: [PATCH 109/113] fix(files): preserve active artifact indexing and narrow helpers --- .../workspace/workspace-file-manager.ts | 25 ++++++- apps/sim/lib/uploads/documents/references.ts | 72 ++++++++++++------- .../workspace-files/search/artifact-ready.ts | 11 ++- .../lib/workspace-files/search/index-state.ts | 18 +++++ .../lib/workspace-files/search/indexing.ts | 3 + .../search/owners.integration.ts | 33 ++++++++- apps/sim/package.json | 1 - bun.lock | 1 - packages/utils/src/errors.test.ts | 26 ------- packages/utils/src/errors.ts | 24 ------- 10 files changed, 134 insertions(+), 80 deletions(-) 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 3da0fabf374..1487c43f5ce 100644 --- a/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts +++ b/apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts @@ -20,7 +20,6 @@ import { getErrorMessage, getPostgresConstraintName, getPostgresErrorCode, - isPostgresCommitRejection, } from '@sim/utils/errors' import { generateShortId } from '@sim/utils/id' import { omit } from '@sim/utils/object' @@ -582,6 +581,30 @@ async function discardStagedFileContent(staged: StagedFileContent): Promise( staged: StagedFileContent, diff --git a/apps/sim/lib/uploads/documents/references.ts b/apps/sim/lib/uploads/documents/references.ts index fa3568646a1..b2c26debdd2 100644 --- a/apps/sim/lib/uploads/documents/references.ts +++ b/apps/sim/lib/uploads/documents/references.ts @@ -1,4 +1,4 @@ -import { tokenizer } from 'acorn' +import ts from '@typescript/typescript6' import { DocCompileUserError } from '@/lib/uploads/documents/compile-error' export type DocumentSourceLanguage = 'javascript' | 'python' @@ -18,31 +18,55 @@ interface SourceToken { const FILE_HELPERS = new Set(['getFileBase64', 'addImage', 'drawImage', 'input_path']) function* javascriptTokens(source: string): Generator { - try { - const tokens = tokenizer(source, { ecmaVersion: 'latest', allowHashBang: true }) - for (let token = tokens.getToken(); token.type.label !== 'eof'; token = tokens.getToken()) { - const label = token.type.label - if (label === 'string') { - yield { - kind: 'string', - value: source.slice(token.start + 1, token.end - 1), - start: token.start + 1, - } - } else if (label === 'template') { - if (source[token.start - 1] === '`' && source[token.end] === '`') { - yield { kind: 'string', value: source.slice(token.start, token.end), start: token.start } - } - } else { - yield { - kind: label === 'name' ? 'word' : 'punctuation', - value: source.slice(token.start, token.end), - start: token.start, - } + const fileName = 'document.js' + const sourceFile = ts.createSourceFile( + fileName, + source, + ts.ScriptTarget.Latest, + true, + ts.ScriptKind.JS + ) + const program = ts.createProgram( + [fileName], + { allowJs: true, noLib: true, noResolve: true }, + { + getSourceFile: (name) => (name === fileName ? sourceFile : undefined), + getDefaultLibFileName: () => '', + writeFile: () => {}, + getCurrentDirectory: () => '', + getDirectories: () => [], + fileExists: (name) => name === fileName, + readFile: (name) => (name === fileName ? source : undefined), + getCanonicalFileName: (name) => name, + useCaseSensitiveFileNames: () => true, + getNewLine: () => '\n', + } + ) + const diagnostic = program.getSyntacticDiagnostics(sourceFile)[0] + if (diagnostic) { + throw new DocCompileUserError(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')) + } + + const pending: ts.Node[] = [sourceFile] + while (pending.length > 0) { + const node = pending.pop() + if (!node) break + if (ts.isStringLiteralLike(node)) { + const start = node.getStart(sourceFile) + 1 + yield { kind: 'string', value: source.slice(start, node.end - 1), start } + continue + } + const children = node.getChildren(sourceFile) + if (children.length > 0) { + for (let index = children.length - 1; index >= 0; index--) pending.push(children[index]) + } else { + const start = node.getStart(sourceFile) + yield { + kind: ts.isIdentifier(node) ? 'word' : 'punctuation', + value: source.slice(start, node.end), + start, } } - } catch (error) { - if (error instanceof SyntaxError) throw new DocCompileUserError(error.message) - throw error } } diff --git a/apps/sim/lib/workspace-files/search/artifact-ready.ts b/apps/sim/lib/workspace-files/search/artifact-ready.ts index 45a6c19c2c2..69927dd5cf0 100644 --- a/apps/sim/lib/workspace-files/search/artifact-ready.ts +++ b/apps/sim/lib/workspace-files/search/artifact-ready.ts @@ -51,11 +51,18 @@ export async function markFileSearchArtifactReadyInTx( const [state] = await tx.select().from(workspaceFileSearchRevision).where(predicate).limit(1) if (state?.buildId) { const [build] = await tx - .select({ artifactKey: workspaceFileSearchBuild.artifactKey }) + .select({ + artifactKey: workspaceFileSearchBuild.artifactKey, + live: sql`${workspaceFileSearchBuild.expiresAt} > clock_timestamp()`, + }) .from(workspaceFileSearchBuild) .where(eq(workspaceFileSearchBuild.id, state.buildId)) .for('update') - if (state.status === 'ready' && build?.artifactKey === input.artifactKey) return false + if ( + build?.artifactKey === input.artifactKey && + (state.status === 'ready' || (state.status === 'pending' && build.live)) + ) + return false await tx .update(workspaceFileSearchBuild) .set({ expiresAt: sql`clock_timestamp()` }) diff --git a/apps/sim/lib/workspace-files/search/index-state.ts b/apps/sim/lib/workspace-files/search/index-state.ts index 09a30cfff43..f555acbd6a9 100644 --- a/apps/sim/lib/workspace-files/search/index-state.ts +++ b/apps/sim/lib/workspace-files/search/index-state.ts @@ -201,6 +201,24 @@ export async function beginFileSearchBuild( }) } +/** Records the selected artifact before extraction so repeated render receipts preserve this build. */ +export async function bindFileSearchBuildArtifact( + build: FileSearchBuild, + artifactKey: string, + dependencies: readonly FileSearchDependencyIdentity[] +): Promise { + return db.transaction(async (tx) => { + await configureFileSearchTransaction(tx, FILE_SEARCH_INDEX_TRANSACTION_LIMITS) + if (!(await lockFileSearchInputs(tx, build, dependencies)) || !(await lockBuild(tx, build))) + return false + await tx + .update(workspaceFileSearchBuild) + .set({ artifactKey }) + .where(eq(workspaceFileSearchBuild.id, build.id)) + return true + }) +} + /** Each batch is fenced and work-bounded; no file or parser work runs inside this transaction. */ export async function appendFileSearchChunks( build: FileSearchBuild, diff --git a/apps/sim/lib/workspace-files/search/indexing.ts b/apps/sim/lib/workspace-files/search/indexing.ts index 0c65187dba8..e9eb480e756 100644 --- a/apps/sim/lib/workspace-files/search/indexing.ts +++ b/apps/sim/lib/workspace-files/search/indexing.ts @@ -28,6 +28,7 @@ import { import { appendFileSearchChunks, beginFileSearchBuild, + bindFileSearchBuildArtifact, type FileSearchBuild, type FileSearchRevision, failFileSearchRevision, @@ -117,6 +118,8 @@ export async function indexWorkspaceFileForSearch( const source = await loadFileSearchSource(revision, signal) if (!source) return const { file, bytes, dependencies, artifactKey } = source + if (artifactKey && !(await bindFileSearchBuildArtifact(build, artifactKey, dependencies))) + return const extracted = await extractIndexText(bytes, file.originalName, signal) if (!extracted) { await publishFileSearchBuild( diff --git a/apps/sim/lib/workspace-files/search/owners.integration.ts b/apps/sim/lib/workspace-files/search/owners.integration.ts index ab2915b1af3..08f5cbb5b9c 100644 --- a/apps/sim/lib/workspace-files/search/owners.integration.ts +++ b/apps/sim/lib/workspace-files/search/owners.integration.ts @@ -324,7 +324,38 @@ describe('owner-scoped indexed file search on PostgreSQL and stored bytes', () = }) expect(served.buffer).toEqual(artifact) expect(served.contributingFiles?.map((file) => file.context)).toEqual([context]) - await indexWorkspaceFileForSearch({ owner, ...(await claim(generated.id)) }, signal) + const extraction = await import('@/lib/workspace-files/search/extract') + const extract = extraction.extractIndexText + const entered = createDeferred() + const release = createDeferred() + const pause = vi + .spyOn(extraction, 'extractIndexText') + .mockImplementationOnce(async (...args) => { + entered.resolve() + await release.promise + return extract(...args) + }) + const indexing = indexWorkspaceFileForSearch( + { owner, ...(await claim(generated.id)) }, + signal + ) + await entered.promise + try { + for (let repeat = 0; repeat < 2; repeat++) { + const cached = await fetchAuthorizedServableWorkspaceFileBuffer( + generated, + f.principal, + { + maxBytes: 1024 * 1024, + } + ) + expect(cached.buffer).toEqual(artifact) + } + } finally { + release.resolve() + await indexing + pause.mockRestore() + } const read = () => searchWorkspaceFileContent.execute({ principal: f.principal, diff --git a/apps/sim/package.json b/apps/sim/package.json index 38c7608ae79..adc051c36ed 100644 --- a/apps/sim/package.json +++ b/apps/sim/package.json @@ -168,7 +168,6 @@ "@xterm/addon-webgl": "0.19.0", "@xterm/xterm": "6.0.0", "@xyflow/react": "12.11.3", - "acorn": "8.17.0", "ajv": "8.18.0", "archiver": "8.0.0", "better-auth": "1.6.27", diff --git a/bun.lock b/bun.lock index dbf645c62d7..7d81a32fcad 100644 --- a/bun.lock +++ b/bun.lock @@ -292,7 +292,6 @@ "@xterm/addon-webgl": "0.19.0", "@xterm/xterm": "6.0.0", "@xyflow/react": "12.11.3", - "acorn": "8.17.0", "ajv": "8.18.0", "archiver": "8.0.0", "better-auth": "1.6.27", diff --git a/packages/utils/src/errors.test.ts b/packages/utils/src/errors.test.ts index 8f5361132ca..168afff1818 100644 --- a/packages/utils/src/errors.test.ts +++ b/packages/utils/src/errors.test.ts @@ -4,7 +4,6 @@ import { getPostgresCancellationReason, getPostgresErrorCode, getTransientDatabaseFailure, - isPostgresCommitRejection, } from '@sim/utils/errors' import { describe, expect, it } from 'vitest' @@ -237,28 +236,3 @@ describe('describeError', () => { expect(described?.causeChain?.length).toBeLessThanOrEqual(10) }) }) - -describe('isPostgresCommitRejection', () => { - it('recognizes a deferred constraint rejection through a transaction wrapper', () => { - const rejection = Object.assign(new Error('deferred constraint failed'), { code: '23514' }) - expect(isPostgresCommitRejection(new Error('transaction failed', { cause: rejection }))).toBe( - true - ) - }) - - it.each(['40003', '08007', 'CONNECTION_CLOSED', 'ECONNRESET', undefined])( - 'does not treat uncertain outcome %s as permission to discard staged data', - (code) => { - const error = Object.assign(new Error('commit outcome unavailable'), { code }) - expect(isPostgresCommitRejection(error)).toBe(false) - } - ) - - it('does not replace an outer connection failure with a nested rejection code', () => { - const rejection = Object.assign(new Error('earlier constraint failure'), { code: '23514' }) - const connection = Object.assign(new Error('connection lost', { cause: rejection }), { - code: 'CONNECTION_CLOSED', - }) - expect(isPostgresCommitRejection(connection)).toBe(false) - }) -}) diff --git a/packages/utils/src/errors.ts b/packages/utils/src/errors.ts index 863772624ac..14248fb4e25 100644 --- a/packages/utils/src/errors.ts +++ b/packages/utils/src/errors.ts @@ -30,30 +30,6 @@ export function getPostgresErrorCode(error: unknown): string | undefined { return readPgErrorField(error, 'code') } -const POSTGRES_COMMIT_REJECTION_CODES = new Set([ - '23000', - '23001', - '23502', - '23503', - '23505', - '23514', - '23P01', - '40000', - '40001', - '40002', - '40P01', -]) - -/** - * Recognizes explicit PostgreSQL constraint/rollback rejections at COMMIT. Use only on the - * transaction's rejection after its callback completed, never on errors from post-commit work. - * Unknown outcomes, including 40003 and connection failures, are not safe cleanup authority. - */ -export function isPostgresCommitRejection(error: unknown): boolean { - const code = getPostgresErrorCode(error) - return code !== undefined && POSTGRES_COMMIT_REJECTION_CODES.has(code) -} - const POSTGRES_CANCELLATION_REASONS = [ ['57014', 'canceling statement due to statement timeout', 'statement_timeout'], ['57014', 'canceling statement due to user request', 'user_cancel'], From 90b660cafca302d89a4641368feffd2b5bd5228a Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 13 Oct 2026 23:08:37 -0700 Subject: [PATCH 110/113] refactor(files): narrow ownership triggers and enforce folder edges with foreign keys --- .../__integration__/file-names.integration.ts | 38 ++++ .../db/file-entity-ownership.integration.ts | 3 - ...le-folder-version-ownership.integration.ts | 139 ++++++++------- .../migrations/0422_file_entity_ownership.sql | 13 -- .../0423_file_folder_version_ownership.sql | 107 ++++++------ .../0425_public_share_entity_ownership.sql | 7 +- .../db/migrations/meta/0423_snapshot.json | 72 ++++++++ .../db/migrations/meta/0424_snapshot.json | 72 ++++++++ .../db/migrations/meta/0425_snapshot.json | 72 ++++++++ .../db/migrations/meta/0426_snapshot.json | 72 ++++++++ .../db/migrations/meta/0427_snapshot.json | 72 ++++++++ .../db/public-share-ownership.integration.ts | 163 ++++++++++-------- packages/db/schema.ts | 16 ++ .../0033_validate_file_ownership.ts | 4 +- 14 files changed, 637 insertions(+), 213 deletions(-) diff --git a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts index d7ee08d0987..11b6a381928 100644 --- a/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts +++ b/apps/sim/lib/uploads/contexts/workspace/__integration__/file-names.integration.ts @@ -190,6 +190,44 @@ describe('workspace file names in PostgreSQL', () => { ) }) + it('serializes crossed folder moves and rejects the move that would close a cycle', async () => { + const fixture = await seedWorkspace() + const first = await createWorkspaceFileFolder({ + workspaceId: fixture.workspaceId, + userId: fixture.aliceId, + name: 'First', + }) + const second = await createWorkspaceFileFolder({ + workspaceId: fixture.workspaceId, + userId: fixture.aliceId, + name: 'Second', + }) + const outcomes = await Promise.allSettled([ + updateWorkspaceFileFolder({ + workspaceId: fixture.workspaceId, + folderId: first.id, + parentId: second.id, + }), + updateWorkspaceFileFolder({ + workspaceId: fixture.workspaceId, + folderId: second.id, + parentId: first.id, + }), + ]) + expect(outcomes.filter((outcome) => outcome.status === 'fulfilled')).toHaveLength(1) + const rejected = outcomes.find((outcome) => outcome.status === 'rejected') + expect(rejected?.status === 'rejected' && rejected.reason).toMatchObject({ + code: 'validation', + message: 'Cannot move a folder into one of its descendants', + }) + const parents = [ + ...(await db.execute<{ id: string; parent_id: string | null }>(sql` + SELECT id, parent_id FROM folder WHERE id IN (${first.id}, ${second.id})`)), + ] + expect(parents.filter((row) => row.parent_id === null)).toHaveLength(1) + expect(parents.filter((row) => row.parent_id !== null)).toHaveLength(1) + }) + function upload(workspaceId: string, userId: string, name: string, folderId?: string | null) { return uploadWorkspaceFile(workspaceId, userId, Buffer.from(name), name, 'text/plain', { folderId, diff --git a/packages/db/file-entity-ownership.integration.ts b/packages/db/file-entity-ownership.integration.ts index 9923bfda6c4..cca27b634df 100644 --- a/packages/db/file-entity-ownership.integration.ts +++ b/packages/db/file-entity-ownership.integration.ts @@ -292,9 +292,6 @@ describe('file entity ownership migration in PostgreSQL', () => { await expect( sql`UPDATE workspace_files SET project_id = NULL WHERE id = 'file'` ).rejects.toMatchObject({ code: '23514' }) - await expect( - sql`UPDATE workspace_files SET project_id = 'project-b' WHERE id = 'file'` - ).rejects.toMatchObject({ code: '23514' }) await expect(sql`DELETE FROM project WHERE id = 'project-a'`).rejects.toMatchObject({ code: '23503', }) diff --git a/packages/db/file-folder-version-ownership.integration.ts b/packages/db/file-folder-version-ownership.integration.ts index 6d30e6a5710..9c7a6d5a7ba 100644 --- a/packages/db/file-folder-version-ownership.integration.ts +++ b/packages/db/file-folder-version-ownership.integration.ts @@ -107,7 +107,7 @@ describe('file folder and version ownership in PostgreSQL', () => { it('preserves metadata and content changes on existing workspace files with retained versions', async () => { await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) VALUES ('file', 'workspace', 'user-a', 'workspace-a')` - await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` + await sql`INSERT INTO workspace_file_version (id, file_id, workspace_id) VALUES ('version', 'file', 'workspace-a')` await sql`UPDATE workspace_files SET key = 'new-revision-key', original_name = 'renamed.md' WHERE id = 'file'` const [file] = await sql`SELECT workspace_id, project_id, key, secret_provenance_version FROM workspace_files` @@ -181,10 +181,10 @@ describe('file folder and version ownership in PostgreSQL', () => { ('workflow-folder', 'Workflows', 'user-a', 'workspace-a', 'workflow')` await expect( sql`UPDATE folder SET parent_id = 'other-folder' WHERE id = 'file-folder'` - ).rejects.toMatchObject({ code: '23514' }) + ).rejects.toMatchObject({ code: '23503' }) await expect( sql`UPDATE folder SET parent_id = 'workflow-folder' WHERE id = 'file-folder'` - ).rejects.toMatchObject({ code: '23514' }) + ).rejects.toMatchObject({ code: '23503' }) await expect( sql`UPDATE folder SET workspace_id = 'workspace-b' WHERE id = 'file-folder'` ).rejects.toMatchObject({ code: '23514' }) @@ -196,24 +196,54 @@ describe('file folder and version ownership in PostgreSQL', () => { ).rejects.toMatchObject({ code: '23514' }) }) - it('supports child-before-parent bulk creation and rejects cycles after the complete statement', async () => { + it('supports child-before-parent bulk creation with matching ownership', async () => { await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, parent_id) VALUES ('child', 'Child', 'user-a', 'workspace-a', 'file', 'parent'), ('parent', 'Parent', 'user-a', 'workspace-a', 'file', NULL)` + expect(await sql`SELECT id, parent_id FROM folder ORDER BY id`).toEqual([ + { id: 'child', parent_id: 'parent' }, + { id: 'parent', parent_id: null }, + ]) + await expect(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, parent_id) + VALUES ('wrong-child', 'Wrong child', 'user-a', 'workspace-a', 'file', 'wrong-parent'), + ('wrong-parent', 'Wrong parent', 'user-a', 'workspace-b', 'file', NULL)`).rejects.toMatchObject( + { code: '23503' } + ) + }) + + it.each(['workspace', 'project'] as const)( + 'parent deletion detaches %s children without changing their owner or content', + async (kind) => { + const workspaceId = kind === 'workspace' ? 'workspace-a' : null + const projectId = kind === 'project' ? 'project-a' : null + await sql`INSERT INTO folder (id, name, user_id, workspace_id, project_id, resource_type, parent_id) + VALUES ('parent', 'Parent', 'user-a', ${workspaceId}, ${projectId}, 'file', NULL), + ('child', 'Child', 'user-a', ${workspaceId}, ${projectId}, 'file', 'parent')` + await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id, project_id, folder_id) + VALUES ('file', ${kind}, 'user-a', ${workspaceId}, ${projectId}, 'child')` + await sql`DELETE FROM folder WHERE id = 'parent'` + expect( + await sql`SELECT parent_id, workspace_id, project_id FROM folder WHERE id = 'child'` + ).toEqual([{ parent_id: null, workspace_id: workspaceId, project_id: projectId }]) + await sql`DELETE FROM folder WHERE id = 'child'` + expect(await sql`SELECT folder_id, key FROM workspace_files WHERE id = 'file'`).toEqual([ + { folder_id: null, key: 'unchanged-object-key' }, + ]) + } + ) + + it('preserves Project ownership without history through attribution and folder changes', async () => { + await sql`INSERT INTO workspace_files (id, context, user_id, project_id) + VALUES ('file', 'project', 'user-a', 'project-a')` + await sql`UPDATE workspace_files SET user_id = 'user-b', deleted_at = now() WHERE id = 'file'` await expect( - sql`UPDATE folder SET parent_id = 'child' WHERE id = 'parent'` + sql`UPDATE workspace_files SET project_id = 'project-b' WHERE id = 'file'` ).rejects.toMatchObject({ code: '23514' }) - await expect(sql`UPDATE folder SET parent_id = id WHERE id = 'parent'`).rejects.toMatchObject({ - code: '23514', - }) - await expect(sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, parent_id) - VALUES ('cycle-a', 'A', 'user-a', 'workspace-a', 'file', 'cycle-b'), - ('cycle-b', 'B', 'user-a', 'workspace-a', 'file', 'cycle-a')`).rejects.toMatchObject({ - code: '23514', - }) - expect(await sql`SELECT id FROM folder ORDER BY id`).toEqual([ - { id: 'child' }, - { id: 'parent' }, + await expect( + sql`UPDATE workspace_files SET project_id = NULL, workspace_id = 'workspace-a', context = 'workspace' WHERE id = 'file'` + ).rejects.toMatchObject({ code: '23514' }) + expect(await sql`SELECT project_id, user_id FROM workspace_files WHERE id = 'file'`).toEqual([ + { project_id: 'project-a', user_id: 'user-b' }, ]) }) @@ -224,7 +254,7 @@ describe('file folder and version ownership in PostgreSQL', () => { VALUES ('project-folder', 'Docs', 'user-a', 'file', 'same-id')` await expect( sql`UPDATE folder SET parent_id = 'workspace-folder' WHERE id = 'project-folder'` - ).rejects.toMatchObject({ code: '23514' }) + ).rejects.toMatchObject({ code: '23503' }) await expect(sql`INSERT INTO folder (id, name, user_id, resource_type, project_id) VALUES ('duplicate', 'Docs', 'user-a', 'file', 'same-id')`).rejects.toMatchObject({ code: '23505', @@ -286,15 +316,17 @@ describe('file folder and version ownership in PostgreSQL', () => { await sql`DELETE FROM project WHERE id = 'project-a'` }) - it('derives version ownership from the file and refuses incompatible legacy pointers', async () => { + it('requires explicit version ownership matching the canonical file', async () => { await sql`INSERT INTO workspace_files (id, context, user_id, workspace_id) VALUES ('workspace-file', 'workspace', 'user-a', 'workspace-a'), ('chat-file', 'mothership', 'user-a', 'workspace-a')` await sql`INSERT INTO workspace_files (id, context, user_id, project_id) VALUES ('project-file', 'project', 'user-a', 'project-a')` await sql`INSERT INTO workspace_files (id, context, user_id) VALUES ('personal', 'copilot', 'user-a')` - await sql`INSERT INTO workspace_file_version (id, file_id) - VALUES ('workspace-version', 'workspace-file'), ('project-version', 'project-file')` + await expect(sql`INSERT INTO workspace_file_version (id, file_id) + VALUES ('missing-owner', 'workspace-file')`).rejects.toMatchObject({ code: '23514' }) + await sql`INSERT INTO workspace_file_version (id, file_id, workspace_id) + VALUES ('workspace-version', 'workspace-file', 'workspace-a'), ('project-version', 'project-file', NULL)` expect(await sql`SELECT id, workspace_id FROM workspace_file_version ORDER BY id`).toEqual([ { id: 'project-version', workspace_id: null }, { id: 'workspace-version', workspace_id: 'workspace-a' }, @@ -324,56 +356,29 @@ describe('file folder and version ownership in PostgreSQL', () => { expect(await sql`SELECT id FROM workspace_file_version`).toHaveLength(0) }) - it.each([ - ['workspace', 'read committed'], - ['workspace', 'repeatable read'], - ['project', 'read committed'], - ['project', 'repeatable read'], - ])( - 'prevents concurrent crossed folder moves for %s at %s isolation', - async (ownerType, isolation) => { - const ownerId = ownerType === 'workspace' ? 'workspace-a' : 'project-a' - const workspaceId = ownerType === 'workspace' ? ownerId : null - const projectId = ownerType === 'project' ? ownerId : null - const lockKey = `resource_folders:file:${ownerType === 'workspace' ? ownerId : `${ownerType}:${ownerId}`}` - await sql`INSERT INTO folder (id, name, user_id, workspace_id, resource_type, project_id) - VALUES ('folder-a', 'A', 'user-a', ${workspaceId}, 'file', ${projectId}), - ('folder-b', 'B', 'user-a', ${workspaceId}, 'file', ${projectId})` - const moved = createDeferred() - const release = createDeferred() - const firstMove = sql.begin(async (tx) => { - await tx`SELECT pg_advisory_xact_lock(hashtextextended(${lockKey}, 0))` - await tx`UPDATE folder SET parent_id = 'folder-b' WHERE id = 'folder-a'` - const [writer] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` - moved.resolve(writer.pid) - await release.promise + it('does not take the application hierarchy lock for direct folder inserts', async () => { + const locked = createDeferred() + const release = createDeferred() + const holder = sql.begin(async (tx) => { + await tx`SELECT pg_advisory_xact_lock(hashtextextended('resource_folders:file:workspace-a', 0))` + locked.resolve() + await release.promise + }) + try { + await locked.promise + await sql.begin(async (tx) => { + await tx`SET LOCAL lock_timeout = '100ms'` + await tx`INSERT INTO folder (id, name, user_id, workspace_id, resource_type) + VALUES ('independent-folder', 'Independent', 'user-a', 'workspace-a', 'file')` }) - const waiting = createDeferred() - let rejection: Promise | undefined - try { - await moved.promise - const secondMove = sql.begin(`isolation level ${isolation}`, async (tx) => { - await tx`SET LOCAL statement_timeout = '5s'` - const [backend] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` - waiting.resolve(backend.pid) - await tx`SELECT pg_advisory_xact_lock(hashtextextended(${lockKey}, 0))` - await tx`UPDATE folder SET parent_id = 'folder-a' WHERE id = 'folder-b'` - }) - rejection = expect(secondMove).rejects.toMatchObject({ - code: isolation === 'repeatable read' ? '40001' : '23514', - }) - await waitForDatabaseLock(await waiting.promise, await moved.promise) - } finally { - release.resolve() - await firstMove - await rejection - } - expect(await sql`SELECT id, parent_id FROM folder ORDER BY id`).toEqual([ - { id: 'folder-a', parent_id: 'folder-b' }, - { id: 'folder-b', parent_id: null }, + expect(await sql`SELECT id FROM folder WHERE id = 'independent-folder'`).toEqual([ + { id: 'independent-folder' }, ]) + } finally { + release.resolve() + await holder } - ) + }) it.each(['read committed', 'repeatable read'])( 'prevents an owner change from racing a retained version at %s isolation', diff --git a/packages/db/migrations/0422_file_entity_ownership.sql b/packages/db/migrations/0422_file_entity_ownership.sql index 0974c50f947..6004fade647 100644 --- a/packages/db/migrations/0422_file_entity_ownership.sql +++ b/packages/db/migrations/0422_file_entity_ownership.sql @@ -40,19 +40,6 @@ LANGUAGE sql IMMUTABLE AS $$ AND file_context IN ('copilot', 'profile-pictures') THEN file_user_id END $$;--> statement-breakpoint -CREATE OR REPLACE FUNCTION workspace_files_preserve_project_owner() -RETURNS trigger LANGUAGE plpgsql AS $$ -BEGIN - IF OLD.project_id IS NOT NULL AND NEW.project_id IS DISTINCT FROM OLD.project_id THEN - RAISE EXCEPTION 'Project file ownership cannot change through a metadata update' USING ERRCODE = '23514'; - END IF; - RETURN NEW; -END; -$$;--> statement-breakpoint -DROP TRIGGER IF EXISTS workspace_files_preserve_project_owner ON workspace_files;--> statement-breakpoint -CREATE TRIGGER workspace_files_preserve_project_owner -BEFORE UPDATE OF project_id ON workspace_files -FOR EACH ROW EXECUTE FUNCTION workspace_files_preserve_project_owner();--> statement-breakpoint CREATE OR REPLACE FUNCTION project_file_uploader_delete_guard() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN diff --git a/packages/db/migrations/0423_file_folder_version_ownership.sql b/packages/db/migrations/0423_file_folder_version_ownership.sql index e1f3bcc4c4e..197a71e08f2 100644 --- a/packages/db/migrations/0423_file_folder_version_ownership.sql +++ b/packages/db/migrations/0423_file_folder_version_ownership.sql @@ -13,69 +13,18 @@ DO $$ BEGIN END $$;--> statement-breakpoint CREATE OR REPLACE FUNCTION folder_validate_owner() RETURNS trigger LANGUAGE plpgsql AS $$ -DECLARE hierarchy_changed boolean := true; lock_key text; BEGIN - IF TG_OP = 'UPDATE' THEN - IF ROW(OLD.workspace_id, OLD.project_id, OLD.resource_type, OLD.id) - IS DISTINCT FROM ROW(NEW.workspace_id, NEW.project_id, NEW.resource_type, NEW.id) THEN - RAISE EXCEPTION 'Folder identity, owner, and resource type cannot be changed' USING ERRCODE = '23514'; - END IF; - hierarchy_changed := NEW.parent_id IS DISTINCT FROM OLD.parent_id; - END IF; - IF hierarchy_changed THEN - -- Existing workspace writers use this key before taking folder row locks. - lock_key := 'resource_folders:' || NEW.resource_type::text || ':' || - coalesce(NEW.workspace_id, 'project:' || NEW.project_id); - PERFORM pg_advisory_xact_lock(hashtextextended(lock_key, 0)); + IF ROW(OLD.workspace_id, OLD.project_id, OLD.resource_type, OLD.id) + IS DISTINCT FROM ROW(NEW.workspace_id, NEW.project_id, NEW.resource_type, NEW.id) THEN + RAISE EXCEPTION 'Folder identity, owner, and resource type cannot be changed' USING ERRCODE = '23514'; END IF; RETURN NEW; END; $$;--> statement-breakpoint DROP TRIGGER IF EXISTS folder_validate_owner ON folder;--> statement-breakpoint CREATE TRIGGER folder_validate_owner -BEFORE INSERT OR UPDATE OF id, workspace_id, project_id, resource_type, parent_id +BEFORE UPDATE OF id, workspace_id, project_id, resource_type ON folder FOR EACH ROW EXECUTE FUNCTION folder_validate_owner();--> statement-breakpoint -CREATE OR REPLACE FUNCTION folder_parent_resource_type_match() -RETURNS trigger LANGUAGE plpgsql AS $$ -DECLARE - child folder%ROWTYPE; - ancestor folder%ROWTYPE; - ancestor_id text; - seen text[]; -BEGIN - SELECT * INTO child FROM folder WHERE id = NEW.id; - IF NOT FOUND THEN RETURN NEW; END IF; - IF TG_OP = 'UPDATE' AND OLD.parent_id IS NOT DISTINCT FROM child.parent_id - AND OLD.resource_type = child.resource_type - AND ROW(OLD.workspace_id, OLD.project_id) - IS NOT DISTINCT FROM ROW(child.workspace_id, child.project_id) THEN - RETURN NEW; - END IF; - ancestor_id := child.parent_id; - seen := ARRAY[child.id]; - WHILE ancestor_id IS NOT NULL LOOP - IF ancestor_id = ANY(seen) THEN - RAISE EXCEPTION 'Folder hierarchy contains a cycle' USING ERRCODE = '23514'; - END IF; - seen := array_append(seen, ancestor_id); - -- Lock every ancestor, so a stale REPEATABLE READ traversal cannot miss a concurrent move. - SELECT * INTO ancestor FROM folder WHERE id = ancestor_id FOR SHARE; - IF NOT FOUND THEN RAISE EXCEPTION 'Folder parent does not exist' USING ERRCODE = '23503'; END IF; - IF ancestor.resource_type <> child.resource_type - OR ROW(ancestor.workspace_id, ancestor.project_id) - IS DISTINCT FROM ROW(child.workspace_id, child.project_id) THEN - RAISE EXCEPTION 'Folder parent belongs to a different owner or resource type' USING ERRCODE = '23514'; - END IF; - ancestor_id := ancestor.parent_id; - END LOOP; - RETURN NEW; -END; -$$;--> statement-breakpoint -DROP TRIGGER IF EXISTS folder_parent_resource_type_match ON folder;--> statement-breakpoint --- End-of-statement checks preserve the supported child-before-parent bulk insert order. -CREATE CONSTRAINT TRIGGER folder_parent_resource_type_match -AFTER INSERT OR UPDATE ON folder DEFERRABLE INITIALLY IMMEDIATE -FOR EACH ROW EXECUTE FUNCTION folder_parent_resource_type_match();--> statement-breakpoint CREATE OR REPLACE FUNCTION workspace_files_validate_relations() RETURNS trigger LANGUAGE plpgsql AS $$ DECLARE @@ -88,6 +37,9 @@ BEGIN SELECT entity_type, entity_id INTO current_type, current_id FROM workspace_file_owner(NEW.context, NEW.workspace_id, NEW.project_id, NEW.organization_id, NEW.user_id); IF TG_OP = 'UPDATE' THEN + IF OLD.project_id IS NOT NULL AND NEW.project_id IS DISTINCT FROM OLD.project_id THEN + RAISE EXCEPTION 'Project file ownership cannot change through a metadata update' USING ERRCODE = '23514'; + END IF; SELECT entity_type, entity_id INTO previous_type, previous_id FROM workspace_file_owner(OLD.context, OLD.workspace_id, OLD.project_id, OLD.organization_id, OLD.user_id); IF (ROW(previous_type, previous_id) IS DISTINCT FROM ROW(current_type, current_id) @@ -114,8 +66,11 @@ END; $$;--> statement-breakpoint DROP TRIGGER IF EXISTS workspace_files_validate_relations ON workspace_files;--> statement-breakpoint CREATE CONSTRAINT TRIGGER workspace_files_validate_relations -AFTER INSERT OR UPDATE ON workspace_files DEFERRABLE INITIALLY IMMEDIATE +AFTER INSERT OR UPDATE OF id, folder_id, context, workspace_id, project_id, organization_id, user_id +ON workspace_files DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW EXECUTE FUNCTION workspace_files_validate_relations();--> statement-breakpoint +DROP TRIGGER IF EXISTS workspace_files_preserve_project_owner ON workspace_files;--> statement-breakpoint +DROP FUNCTION IF EXISTS workspace_files_preserve_project_owner();--> statement-breakpoint -- Relationship fences advance the row version without changing durable content or its provenance. -- Real inserts and metadata/content changes must still normalize before downstream triggers run. CREATE OR REPLACE FUNCTION workspace_file_content_version_millisecond() @@ -146,7 +101,6 @@ BEGIN SELECT entity_type, entity_id INTO parent_type, parent_id FROM workspace_file_owner(parent.context, parent.workspace_id, parent.project_id, parent.organization_id, parent.user_id); IF parent_type = 'workspace' AND parent.context IN ('workspace', 'test', 'changelog') THEN - IF NEW.workspace_id IS NULL THEN NEW.workspace_id := parent_id; END IF; IF NEW.workspace_id IS DISTINCT FROM parent_id THEN RAISE EXCEPTION 'Version workspace differs from its file owner' USING ERRCODE = '23514'; END IF; @@ -190,3 +144,42 @@ CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "workspace_files_project_folder_n ON "workspace_files" USING btree ("project_id", coalesce("folder_id", ''), "original_name") WHERE "deleted_at" IS NULL AND "project_id" IS NOT NULL;--> statement-breakpoint SET lock_timeout = '5s'; + +--> statement-breakpoint +DO $$ +DECLARE target text; recovery_name text; +BEGIN + FOREACH target IN ARRAY ARRAY['folder_workspace_owner_type_unique', 'folder_project_owner_type_unique'] LOOP + recovery_name := target || '_failed'; + IF EXISTS (SELECT 1 FROM pg_index WHERE indexrelid = to_regclass(recovery_name) + AND (indisvalid OR indrelid <> 'folder'::regclass)) THEN + RAISE EXCEPTION 'Refusing to drop unexpected recovery index %', recovery_name; + END IF; + IF EXISTS (SELECT 1 FROM pg_index WHERE indexrelid = to_regclass(target) + AND indrelid = 'folder'::regclass AND NOT indisvalid) THEN + EXECUTE format('ALTER INDEX %I RENAME TO %I', target, recovery_name); + END IF; + END LOOP; +END $$;--> statement-breakpoint +COMMIT;--> statement-breakpoint +SET lock_timeout = 0;--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS folder_workspace_owner_type_unique_failed;--> statement-breakpoint +CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS folder_workspace_owner_type_unique +ON folder (id, workspace_id, resource_type);--> statement-breakpoint +DROP INDEX CONCURRENTLY IF EXISTS folder_project_owner_type_unique_failed;--> statement-breakpoint +CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS folder_project_owner_type_unique +ON folder (id, project_id, resource_type);--> statement-breakpoint +SET lock_timeout = '5s';--> statement-breakpoint +-- The exclusive owner check makes exactly one parent-owner FK applicable to every folder. +DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_workspace_parent_owner_fk' AND conrelid = 'folder'::regclass) THEN + ALTER TABLE folder ADD CONSTRAINT folder_workspace_parent_owner_fk + FOREIGN KEY (parent_id, workspace_id, resource_type) REFERENCES folder(id, workspace_id, resource_type) NOT VALID; + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_project_parent_owner_fk' AND conrelid = 'folder'::regclass) THEN + ALTER TABLE folder ADD CONSTRAINT folder_project_parent_owner_fk + FOREIGN KEY (parent_id, project_id, resource_type) REFERENCES folder(id, project_id, resource_type) NOT VALID; + END IF; +END $$;--> statement-breakpoint +DROP TRIGGER IF EXISTS folder_parent_resource_type_match ON folder;--> statement-breakpoint +DROP FUNCTION IF EXISTS folder_parent_resource_type_match(); diff --git a/packages/db/migrations/0425_public_share_entity_ownership.sql b/packages/db/migrations/0425_public_share_entity_ownership.sql index c256694fb84..3379801738b 100644 --- a/packages/db/migrations/0425_public_share_entity_ownership.sql +++ b/packages/db/migrations/0425_public_share_entity_ownership.sql @@ -73,9 +73,10 @@ BEGIN FROM workspace_file_owner(OLD.context, OLD.workspace_id, OLD.project_id, OLD.organization_id, OLD.user_id); SELECT entity_type, entity_id INTO current_type, current_id FROM workspace_file_owner(NEW.context, NEW.workspace_id, NEW.project_id, NEW.organization_id, NEW.user_id); - IF NEW.id IS DISTINCT FROM OLD.id - OR ROW(previous_type, previous_id) IS DISTINCT FROM ROW(current_type, current_id) THEN - DELETE FROM public_share WHERE resource_type = 'file' AND resource_id = OLD.id; + IF (NEW.id IS DISTINCT FROM OLD.id + OR ROW(previous_type, previous_id) IS DISTINCT FROM ROW(current_type, current_id)) + AND EXISTS (SELECT 1 FROM public_share WHERE resource_type = 'file' AND resource_id = OLD.id) THEN + RAISE EXCEPTION 'Revoke file shares explicitly before changing file identity or ownership' USING ERRCODE = '23514'; END IF; RETURN NEW; END; diff --git a/packages/db/migrations/meta/0423_snapshot.json b/packages/db/migrations/meta/0423_snapshot.json index fa37f6f112e..c3a1b6718ad 100644 --- a/packages/db/migrations/meta/0423_snapshot.json +++ b/packages/db/migrations/meta/0423_snapshot.json @@ -8804,6 +8804,60 @@ "concurrently": true, "method": "btree", "with": {} + }, + "folder_workspace_owner_type_unique": { + "name": "folder_workspace_owner_type_unique", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_owner_type_unique": { + "name": "folder_project_owner_type_unique", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -8842,6 +8896,24 @@ "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" + }, + "folder_workspace_parent_owner_fk": { + "name": "folder_workspace_parent_owner_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id", "workspace_id", "resource_type"], + "columnsTo": ["id", "workspace_id", "resource_type"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folder_project_parent_owner_fk": { + "name": "folder_project_parent_owner_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id", "project_id", "resource_type"], + "columnsTo": ["id", "project_id", "resource_type"], + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, diff --git a/packages/db/migrations/meta/0424_snapshot.json b/packages/db/migrations/meta/0424_snapshot.json index 6f464c0f4cc..0ad15739579 100644 --- a/packages/db/migrations/meta/0424_snapshot.json +++ b/packages/db/migrations/meta/0424_snapshot.json @@ -8804,6 +8804,60 @@ "concurrently": true, "method": "btree", "with": {} + }, + "folder_workspace_owner_type_unique": { + "name": "folder_workspace_owner_type_unique", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_owner_type_unique": { + "name": "folder_project_owner_type_unique", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -8842,6 +8896,24 @@ "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" + }, + "folder_workspace_parent_owner_fk": { + "name": "folder_workspace_parent_owner_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id", "workspace_id", "resource_type"], + "columnsTo": ["id", "workspace_id", "resource_type"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folder_project_parent_owner_fk": { + "name": "folder_project_parent_owner_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id", "project_id", "resource_type"], + "columnsTo": ["id", "project_id", "resource_type"], + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, diff --git a/packages/db/migrations/meta/0425_snapshot.json b/packages/db/migrations/meta/0425_snapshot.json index fdcfb8745d1..d163dbb6000 100644 --- a/packages/db/migrations/meta/0425_snapshot.json +++ b/packages/db/migrations/meta/0425_snapshot.json @@ -8804,6 +8804,60 @@ "concurrently": true, "method": "btree", "with": {} + }, + "folder_workspace_owner_type_unique": { + "name": "folder_workspace_owner_type_unique", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_owner_type_unique": { + "name": "folder_project_owner_type_unique", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -8842,6 +8896,24 @@ "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" + }, + "folder_workspace_parent_owner_fk": { + "name": "folder_workspace_parent_owner_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id", "workspace_id", "resource_type"], + "columnsTo": ["id", "workspace_id", "resource_type"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folder_project_parent_owner_fk": { + "name": "folder_project_parent_owner_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id", "project_id", "resource_type"], + "columnsTo": ["id", "project_id", "resource_type"], + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, diff --git a/packages/db/migrations/meta/0426_snapshot.json b/packages/db/migrations/meta/0426_snapshot.json index 166955bc856..0c2450cb6df 100644 --- a/packages/db/migrations/meta/0426_snapshot.json +++ b/packages/db/migrations/meta/0426_snapshot.json @@ -8804,6 +8804,60 @@ "concurrently": true, "method": "btree", "with": {} + }, + "folder_workspace_owner_type_unique": { + "name": "folder_workspace_owner_type_unique", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_owner_type_unique": { + "name": "folder_project_owner_type_unique", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -8842,6 +8896,24 @@ "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" + }, + "folder_workspace_parent_owner_fk": { + "name": "folder_workspace_parent_owner_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id", "workspace_id", "resource_type"], + "columnsTo": ["id", "workspace_id", "resource_type"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folder_project_parent_owner_fk": { + "name": "folder_project_parent_owner_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id", "project_id", "resource_type"], + "columnsTo": ["id", "project_id", "resource_type"], + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, diff --git a/packages/db/migrations/meta/0427_snapshot.json b/packages/db/migrations/meta/0427_snapshot.json index d8d77570594..e424edfa83f 100644 --- a/packages/db/migrations/meta/0427_snapshot.json +++ b/packages/db/migrations/meta/0427_snapshot.json @@ -8804,6 +8804,60 @@ "concurrently": true, "method": "btree", "with": {} + }, + "folder_workspace_owner_type_unique": { + "name": "folder_workspace_owner_type_unique", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": true, + "method": "btree", + "with": {} + }, + "folder_project_owner_type_unique": { + "name": "folder_project_owner_type_unique", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": true, + "method": "btree", + "with": {} } }, "foreignKeys": { @@ -8842,6 +8896,24 @@ "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" + }, + "folder_workspace_parent_owner_fk": { + "name": "folder_workspace_parent_owner_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id", "workspace_id", "resource_type"], + "columnsTo": ["id", "workspace_id", "resource_type"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "folder_project_parent_owner_fk": { + "name": "folder_project_parent_owner_fk", + "tableFrom": "folder", + "tableTo": "folder", + "columnsFrom": ["parent_id", "project_id", "resource_type"], + "columnsTo": ["id", "project_id", "resource_type"], + "onDelete": "no action", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, diff --git a/packages/db/public-share-ownership.integration.ts b/packages/db/public-share-ownership.integration.ts index 1a9c0bdc029..ae6c484eae7 100644 --- a/packages/db/public-share-ownership.integration.ts +++ b/packages/db/public-share-ownership.integration.ts @@ -11,7 +11,7 @@ import { createDeferred } from '@sim/testing/helpers/deferred' import { getErrorMessage } from '@sim/utils/errors' import { sleep } from '@sim/utils/helpers' import { generateId } from '@sim/utils/id' -import postgres, { type Sql } from 'postgres' +import postgres, { type Sql, type TransactionSql } from 'postgres' import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest' const databaseUrl = readTestDatabaseUrl() @@ -165,7 +165,7 @@ describe('Public share canonical ownership in PostgreSQL', () => { VALUES (${id}, ${kind}, 'user-a', ${kind === 'workspace' ? owner : null}, ${kind === 'project' ? owner : null}, ${id})` } function share( - executor: Sql, + executor: Sql | TransactionSql, id = 'share', fileId = 'file', owner = 'project-a', @@ -190,7 +190,8 @@ describe('Public share canonical ownership in PostgreSQL', () => { const before = await sql`SELECT content_updated_at::text, secret_provenance_version, key FROM workspace_files WHERE id = 'file'` if (relationship === 'share') await share(sql, 'share', 'file', 'workspace-a', 'workspace') - else await sql`INSERT INTO workspace_file_version (id, file_id) VALUES ('version', 'file')` + else + await sql`INSERT INTO workspace_file_version (id, file_id, workspace_id) VALUES ('version', 'file', 'workspace-a')` expect( await sql`SELECT content_updated_at::text, secret_provenance_version, key FROM workspace_files WHERE id = 'file'` @@ -279,12 +280,25 @@ describe('Public share canonical ownership in PostgreSQL', () => { ) check( - 'workspace owner changes invalidate existing links and preserve legacy nonfile shares', + 'workspace owner changes are refused while file shares exist, preserving links and configuration', async () => { await file('file', 'workspace-a', 'workspace') await share(sql, 'share', 'file', 'workspace-a', 'workspace') - await sql`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'file'` - expect(await sql`SELECT id FROM public_share`).toEqual([]) + const before = await sql`SELECT * FROM public_share WHERE id = 'share'` + await expect( + sql`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'file'` + ).rejects.toMatchObject({ code: '23514' }) + expect(await sql`SELECT * FROM public_share WHERE id = 'share'`).toEqual(before) + expect(await sql`SELECT workspace_id FROM workspace_files WHERE id = 'file'`).toEqual([ + { workspace_id: 'workspace-a' }, + ]) + await sql.begin(async (tx) => { + await tx`DELETE FROM public_share WHERE id = 'share'` + await tx`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'file'` + }) + expect(await sql`SELECT workspace_id FROM workspace_files WHERE id = 'file'`).toEqual([ + { workspace_id: 'workspace-b' }, + ]) await sql`INSERT INTO public_share (id, resource_type, resource_id, workspace_id, token) VALUES ('legacy', 'folder', 'unregistered-legacy-target', 'workspace-a', 'legacy')` expect(await sql`SELECT entity_type, entity_id FROM public_share`).toEqual([ @@ -505,74 +519,85 @@ describe('Public share canonical ownership in PostgreSQL', () => { ) for (const isolation of ['read committed', 'repeatable read'] as const) { - check( - `${isolation}: file deletion waits for share insertion and cannot leave an orphan`, - async () => { - await file() - const snapshot = createDeferred() - const startDelete = createDeferred() - const pid = createDeferred() - const deleting = sql - .begin(`isolation level ${isolation}`, async (tx) => { - await tx`SELECT count(*) FROM public_share` - snapshot.resolve() - await startDelete.promise - const [connection] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` - pid.resolve(connection.pid) - await tx`DELETE FROM workspace_files WHERE id = 'file'` - }) - .then( - () => null, - (error: unknown) => error - ) - await Promise.race([ - snapshot.promise, - deleting.then((error) => { - if (error) throw error - throw new Error('Deletion ended before acquiring its snapshot') - }), - ]) - const inserted = createDeferred() - const release = createDeferred() - const inserting = sql.begin(async (tx) => { - await tx`INSERT INTO public_share (id, resource_type, resource_id, token, entity_type, entity_id) - VALUES ('share', 'file', 'file', 'share', 'project', 'project-a')` - const [writer] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` - inserted.resolve(writer.pid) - await release.promise - }) - let waited = false - try { - await Promise.race([inserted.promise, inserting]) - startDelete.resolve() - const deletePid = await Promise.race([ - pid.promise, - deleting.then((error) => { + for (const operation of ['deletion', 'owner change'] as const) { + check( + `${isolation}: file ${operation} waits for share insertion and preserves ownership`, + async () => { + await file('file', 'workspace-a', 'workspace') + const snapshot = createDeferred() + const startMutation = createDeferred() + const pid = createDeferred() + const mutating = sql + .begin(`isolation level ${isolation}`, async (tx) => { + await tx`SELECT count(*) FROM public_share` + snapshot.resolve() + await startMutation.promise + const [connection] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + pid.resolve(connection.pid) + if (operation === 'deletion') await tx`DELETE FROM workspace_files WHERE id = 'file'` + else + await tx`UPDATE workspace_files SET workspace_id = 'workspace-b' WHERE id = 'file'` + }) + .then( + () => null, + (error: unknown) => error + ) + await Promise.race([ + snapshot.promise, + mutating.then((error) => { if (error) throw error - throw new Error('Deletion ended before publishing its connection') + throw new Error('Mutation ended before acquiring its snapshot') }), ]) - waited = await waitForDatabaseLock(deletePid, await inserted.promise) - } finally { - startDelete.resolve() - release.resolve() - await inserting - await deleting - } - expect(waited, 'target deletion must wait for share insertion').toBe(true) - const result = await deleting - expect( - await sql`SELECT share.id FROM public_share share + const inserted = createDeferred() + const release = createDeferred() + const inserting = sql.begin(async (tx) => { + await share(tx, 'share', 'file', 'workspace-a', 'workspace') + const [writer] = await tx<{ pid: number }[]>`SELECT pg_backend_pid() AS pid` + inserted.resolve(writer.pid) + await release.promise + }) + let waited = false + try { + await Promise.race([inserted.promise, inserting]) + startMutation.resolve() + const mutationPid = await Promise.race([ + pid.promise, + mutating.then((error) => { + if (error) throw error + throw new Error('Mutation ended before publishing its connection') + }), + ]) + waited = await waitForDatabaseLock(mutationPid, await inserted.promise) + } finally { + startMutation.resolve() + release.resolve() + await inserting + await mutating + } + expect(waited, 'target mutation must wait for share insertion').toBe(true) + const result = await mutating + expect( + await sql`SELECT share.id FROM public_share share LEFT JOIN workspace_files file ON file.id = share.resource_id WHERE share.resource_type = 'file' AND file.id IS NULL` - ).toEqual([]) - if (isolation === 'repeatable read') expect(result).toMatchObject({ code: '40001' }) - else expect(result).toBeNull() - await sql`DELETE FROM workspace_files WHERE id = 'file'` - expect(await sql`SELECT id FROM public_share`).toEqual([]) - } - ) - + ).toEqual([]) + if (isolation === 'repeatable read') expect(result).toMatchObject({ code: '40001' }) + else if (operation === 'deletion') expect(result).toBeNull() + else expect(result).toMatchObject({ code: '23514' }) + if (operation === 'owner change') { + expect(await sql`SELECT workspace_id FROM workspace_files WHERE id = 'file'`).toEqual([ + { workspace_id: 'workspace-a' }, + ]) + expect(await sql`SELECT token, entity_id FROM public_share WHERE id = 'share'`).toEqual( + [{ token: 'share', entity_id: 'workspace-a' }] + ) + } + await sql`DELETE FROM workspace_files WHERE id = 'file'` + expect(await sql`SELECT id FROM public_share`).toEqual([]) + } + ) + } check(`${isolation}: a new share cannot commit behind target deletion`, async () => { await file() const deleted = createDeferred() diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 426ce22f985..25831cb9831 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -277,6 +277,22 @@ export const folder = pgTable( 'folder_owner_check', sql`num_nonnulls(${table.workspaceId}, ${table.projectId}) = 1 AND (${table.projectId} IS NULL OR ${table.resourceType} = 'file')` ), + workspaceOwnerTypeUnique: uniqueIndex('folder_workspace_owner_type_unique') + .on(table.id, table.workspaceId, table.resourceType) + .concurrently(), + workspaceParentOwnerFk: foreignKey({ + name: 'folder_workspace_parent_owner_fk', + columns: [table.parentId, table.workspaceId, table.resourceType], + foreignColumns: [table.id, table.workspaceId, table.resourceType], + }), + projectOwnerTypeUnique: uniqueIndex('folder_project_owner_type_unique') + .on(table.id, table.projectId, table.resourceType) + .concurrently(), + projectParentOwnerFk: foreignKey({ + name: 'folder_project_parent_owner_fk', + columns: [table.parentId, table.projectId, table.resourceType], + foreignColumns: [table.id, table.projectId, table.resourceType], + }), projectIdIdx: index('folder_project_id_idx').on(table.projectId, table.id).concurrently(), projectResourceParentNameActiveUnique: uniqueIndex( 'folder_project_resource_parent_name_active_unique' diff --git a/packages/db/script-migrations/0033_validate_file_ownership.ts b/packages/db/script-migrations/0033_validate_file_ownership.ts index 0e46768f3b3..34e74bfa88a 100644 --- a/packages/db/script-migrations/0033_validate_file_ownership.ts +++ b/packages/db/script-migrations/0033_validate_file_ownership.ts @@ -16,7 +16,9 @@ export async function validateFileOwnership(sql: Sql) { VALIDATE CONSTRAINT workspace_files_project_binding_check` await tx`ALTER TABLE folder VALIDATE CONSTRAINT folder_project_id_project_id_fk, - VALIDATE CONSTRAINT folder_owner_check` + VALIDATE CONSTRAINT folder_owner_check, + VALIDATE CONSTRAINT folder_workspace_parent_owner_fk, + VALIDATE CONSTRAINT folder_project_parent_owner_fk` }) let afterVersionId = '' From b117b6a92bbbcfbe89d26241e2c174b1a12b4939 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 13 Oct 2026 23:08:38 -0700 Subject: [PATCH 111/113] fix(realtime): preserve replacement documents during delayed joins --- .../handlers/file-doc-project.integration.ts | 89 ++++++++++++++++++- apps/realtime/src/handlers/file-doc.ts | 24 +++-- 2 files changed, 102 insertions(+), 11 deletions(-) diff --git a/apps/realtime/src/handlers/file-doc-project.integration.ts b/apps/realtime/src/handlers/file-doc-project.integration.ts index 6b5e3f8531a..b51d5e0528d 100644 --- a/apps/realtime/src/handlers/file-doc-project.integration.ts +++ b/apps/realtime/src/handlers/file-doc-project.integration.ts @@ -31,6 +31,7 @@ import { flushAllFileDocRooms, setupWorkspaceFileDocHandlers, } from '@/handlers/file-doc' +import { getFileDocStore, initFileDocStore } from '@/handlers/file-doc-store' import type { AuthenticatedSocket } from '@/middleware/auth' import { beginRoomPermissionRead, @@ -42,6 +43,7 @@ import { MemoryRoomManager } from '@/rooms' const projectId = generateId() const fileId = generateId() let docId = generateId() +let version = 1 const document = new Y.Doc() document.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.flag, true) document.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.docIdKey, docId) @@ -52,6 +54,13 @@ const actors = new Map([ ['writer', 'write'], ]) const checks: { name: string; status: string; durationMs: number; error?: string }[] = [] +const delayedAccess = new Map< + string, + { + entered: ReturnType> + release: ReturnType> + } +>() let app: HttpServer let http: HttpServer let io: Server @@ -75,7 +84,7 @@ async function listen(server: HttpServer) { } beforeAll(async () => { - env.REDIS_URL = undefined + await initFileDocStore(env.REDIS_URL) app = createServer(async (request, response) => { const actor = request.headers['x-sim-subject-user-id'] const connection = request.headers['x-sim-realtime-connection-id'] @@ -108,13 +117,26 @@ beforeAll(async () => { return } if (request.url.endsWith('/access')) { + const authorizedDocId = docId + const delayed = delayedAccess.get(actor) + if (delayed) { + delayedAccess.delete(actor) + delayed.entered.resolve() + await delayed.release.promise + } response.end( - JSON.stringify({ projectId, fileId, canRead: true, canWrite: role === 'write', docId }) + JSON.stringify({ + projectId, + fileId, + canRead: true, + canWrite: role === 'write', + docId: authorizedDocId, + }) ) return } if (request.url.endsWith('/seed')) { - response.end(JSON.stringify({ update: seed, version: 1 })) + response.end(JSON.stringify({ update: seed, version })) return } if (request.url.endsWith('/persist')) { @@ -123,7 +145,8 @@ beforeAll(async () => { return } capturedActors.push(actor) - response.end(JSON.stringify({ status: 'persisted', version: 2 })) + version += 1 + response.end(JSON.stringify({ status: 'persisted', version })) return } response.writeHead(404).end('{}') @@ -677,6 +700,63 @@ describe('Project documents across the Socket.IO and internal HTTP boundary', () expect(restored.joined.docId).toBe(docId) } ) + check('a delayed authorization cannot retire a replacement with acknowledged edits', async () => { + await join('reader') + const actor = `delayed-${generateId()}` + actors.set(actor, 'read') + const gate = { entered: createDeferred(), release: createDeferred() } + delayedAccess.set(actor, gate) + const delayed = await startJoin(actor) + const outcome = delayed.joined.then( + () => null, + (error: unknown) => error + ) + const replacement = new Y.Doc() + try { + await gate.entered.promise + docId = generateId() + replacement.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.flag, true) + replacement.getMap(FILE_DOC_SEED.configMap).set(FILE_DOC_SEED.docIdKey, docId) + seed = Buffer.from(Y.encodeStateAsUpdate(replacement)).toString('base64') + const writerActor = `replacement-${generateId()}` + actors.set(writerActor, 'write') + const writer = await join(writerActor) + expect(writer.joined.docId).toBe(docId) + replacement.getText('body').insert(0, 'acknowledged replacement edit') + expect( + await writer.socket.timeout(1500).emitWithAck(FILE_DOC_EVENTS.UPDATE, { + fileId, + projectId, + docId, + updateId: generateId(), + update: Y.encodeStateAsUpdate(replacement), + }) + ).toMatchObject({ status: 'accepted' }) + gate.release.resolve() + await outcome + const name = `project-file-doc:${projectId}/${fileId}` + const store = getFileDocStore() + expect(await store.getDocumentGeneration(name)).toBe(docId) + const snapshot = await store.getStreamSnapshot(name, docId) + expect(snapshot).not.toBeNull() + const recovered = new Y.Doc() + try { + if (!snapshot) throw new Error('Acknowledged replacement history disappeared') + Y.applyUpdate(recovered, snapshot.docState) + expect(recovered.getText('body').toString()).toBe('acknowledged replacement edit') + } finally { + recovered.destroy() + } + expect((await join('reader')).joined.docId).toBe(docId) + await flushAllFileDocRooms() + expect(capturedActors.at(-1)).toBe(writerActor) + } finally { + gate.release.resolve() + await outcome + delayedAccess.delete(actor) + replacement.destroy() + } + }) }) afterAll(async () => { @@ -684,6 +764,7 @@ afterAll(async () => { await flushAllFileDocRooms() await new Promise((resolve) => io.close(() => resolve())) await new Promise((resolve) => app.close(() => resolve())) + await getFileDocStore().shutdown() document.destroy() const reportPath = process.env.PROJECT_FILE_SOCKET_REPORT_PATH ?? resolve('test-results/project-file-socket.json') diff --git a/apps/realtime/src/handlers/file-doc.ts b/apps/realtime/src/handlers/file-doc.ts index 418f7b81b2b..ec1a64791c0 100644 --- a/apps/realtime/src/handlers/file-doc.ts +++ b/apps/realtime/src/handlers/file-doc.ts @@ -1621,6 +1621,9 @@ export function setupWorkspaceFileDocHandlers( emitError: ({ error, code, retryable }) => emitJoinError(socket, fileId, clientId, error, code, retryable), }) + const store = getFileDocStore() + // A delayed authorization may retire only the generation observed before that lookup. + const observedGeneration = projectId ? await store.getDocumentGeneration(name) : '' const authorized = await authorizeJoin() if (!authorized) return @@ -1628,23 +1631,30 @@ export function setupWorkspaceFileDocHandlers( // awareness). Resolved here so the generation guard below also covers this await. const avatarUrl = await resolveAvatarUrl(socket, userId) - const store = getFileDocStore() if (projectId && authorized.docId) { - const shared = await store.getDocumentGeneration(name) - if (shared && shared !== authorized.docId) { + if (observedGeneration && observedGeneration !== authorized.docId) { await retireLiveProjectFileDocument( { projectId, fileId, - retiredDocId: shared, + retiredDocId: observedGeneration, replacementDocId: authorized.docId, }, io ) } - const local = fileDocRooms.get(name) - if (local && docIdOf(local.doc) && docIdOf(local.doc) !== authorized.docId) - discardInvalidatedRoom(name, io) + const currentGeneration = await store.getDocumentGeneration(name) + if (currentGeneration && currentGeneration !== authorized.docId) { + emitJoinError( + socket, + fileId, + clientId, + 'Document changed while joining', + 'JOIN_FAILED', + true + ) + return + } } const existing = fileDocRooms.get(name) if ( From 686bd0e0a4b8554253dc391fb5bca2c8d1d8663a Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 13 Oct 2026 23:21:55 -0700 Subject: [PATCH 112/113] fix(files): provision composite folder keys before references --- .../0423_file_folder_version_ownership.sql | 6 ++ .../db/migrations/meta/0423_snapshot.json | 67 ++++--------------- .../db/migrations/meta/0424_snapshot.json | 67 ++++--------------- .../db/migrations/meta/0425_snapshot.json | 67 ++++--------------- .../db/migrations/meta/0426_snapshot.json | 67 ++++--------------- .../db/migrations/meta/0427_snapshot.json | 67 ++++--------------- packages/db/schema.ts | 16 +++-- packages/db/scripts/push.integration.ts | 23 +++++++ patches/drizzle-kit@0.31.10.patch | 14 ++++ 9 files changed, 113 insertions(+), 281 deletions(-) diff --git a/packages/db/migrations/0423_file_folder_version_ownership.sql b/packages/db/migrations/0423_file_folder_version_ownership.sql index 197a71e08f2..e5babf7970b 100644 --- a/packages/db/migrations/0423_file_folder_version_ownership.sql +++ b/packages/db/migrations/0423_file_folder_version_ownership.sql @@ -172,6 +172,12 @@ ON folder (id, project_id, resource_type);--> statement-breakpoint SET lock_timeout = '5s';--> statement-breakpoint -- The exclusive owner check makes exactly one parent-owner FK applicable to every folder. DO $$ BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_workspace_owner_type_unique' AND conrelid = 'folder'::regclass) THEN + ALTER TABLE folder ADD CONSTRAINT folder_workspace_owner_type_unique UNIQUE USING INDEX folder_workspace_owner_type_unique; + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_project_owner_type_unique' AND conrelid = 'folder'::regclass) THEN + ALTER TABLE folder ADD CONSTRAINT folder_project_owner_type_unique UNIQUE USING INDEX folder_project_owner_type_unique; + END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'folder_workspace_parent_owner_fk' AND conrelid = 'folder'::regclass) THEN ALTER TABLE folder ADD CONSTRAINT folder_workspace_parent_owner_fk FOREIGN KEY (parent_id, workspace_id, resource_type) REFERENCES folder(id, workspace_id, resource_type) NOT VALID; diff --git a/packages/db/migrations/meta/0423_snapshot.json b/packages/db/migrations/meta/0423_snapshot.json index c3a1b6718ad..d9b8aa328a9 100644 --- a/packages/db/migrations/meta/0423_snapshot.json +++ b/packages/db/migrations/meta/0423_snapshot.json @@ -8804,60 +8804,6 @@ "concurrently": true, "method": "btree", "with": {} - }, - "folder_workspace_owner_type_unique": { - "name": "folder_workspace_owner_type_unique", - "columns": [ - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "workspace_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_project_owner_type_unique": { - "name": "folder_project_owner_type_unique", - "columns": [ - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "project_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": true, - "method": "btree", - "with": {} } }, "foreignKeys": { @@ -8917,7 +8863,18 @@ } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {}, + "uniqueConstraints": { + "folder_workspace_owner_type_unique": { + "name": "folder_workspace_owner_type_unique", + "nullsNotDistinct": false, + "columns": ["id", "workspace_id", "resource_type"] + }, + "folder_project_owner_type_unique": { + "name": "folder_project_owner_type_unique", + "nullsNotDistinct": false, + "columns": ["id", "project_id", "resource_type"] + } + }, "policies": {}, "checkConstraints": { "folder_owner_check": { diff --git a/packages/db/migrations/meta/0424_snapshot.json b/packages/db/migrations/meta/0424_snapshot.json index 0ad15739579..f7a2db63fc2 100644 --- a/packages/db/migrations/meta/0424_snapshot.json +++ b/packages/db/migrations/meta/0424_snapshot.json @@ -8804,60 +8804,6 @@ "concurrently": true, "method": "btree", "with": {} - }, - "folder_workspace_owner_type_unique": { - "name": "folder_workspace_owner_type_unique", - "columns": [ - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "workspace_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_project_owner_type_unique": { - "name": "folder_project_owner_type_unique", - "columns": [ - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "project_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": true, - "method": "btree", - "with": {} } }, "foreignKeys": { @@ -8917,7 +8863,18 @@ } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {}, + "uniqueConstraints": { + "folder_workspace_owner_type_unique": { + "name": "folder_workspace_owner_type_unique", + "nullsNotDistinct": false, + "columns": ["id", "workspace_id", "resource_type"] + }, + "folder_project_owner_type_unique": { + "name": "folder_project_owner_type_unique", + "nullsNotDistinct": false, + "columns": ["id", "project_id", "resource_type"] + } + }, "policies": {}, "checkConstraints": { "folder_owner_check": { diff --git a/packages/db/migrations/meta/0425_snapshot.json b/packages/db/migrations/meta/0425_snapshot.json index d163dbb6000..3b2d8519199 100644 --- a/packages/db/migrations/meta/0425_snapshot.json +++ b/packages/db/migrations/meta/0425_snapshot.json @@ -8804,60 +8804,6 @@ "concurrently": true, "method": "btree", "with": {} - }, - "folder_workspace_owner_type_unique": { - "name": "folder_workspace_owner_type_unique", - "columns": [ - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "workspace_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_project_owner_type_unique": { - "name": "folder_project_owner_type_unique", - "columns": [ - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "project_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": true, - "method": "btree", - "with": {} } }, "foreignKeys": { @@ -8917,7 +8863,18 @@ } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {}, + "uniqueConstraints": { + "folder_workspace_owner_type_unique": { + "name": "folder_workspace_owner_type_unique", + "nullsNotDistinct": false, + "columns": ["id", "workspace_id", "resource_type"] + }, + "folder_project_owner_type_unique": { + "name": "folder_project_owner_type_unique", + "nullsNotDistinct": false, + "columns": ["id", "project_id", "resource_type"] + } + }, "policies": {}, "checkConstraints": { "folder_owner_check": { diff --git a/packages/db/migrations/meta/0426_snapshot.json b/packages/db/migrations/meta/0426_snapshot.json index 0c2450cb6df..122957af135 100644 --- a/packages/db/migrations/meta/0426_snapshot.json +++ b/packages/db/migrations/meta/0426_snapshot.json @@ -8804,60 +8804,6 @@ "concurrently": true, "method": "btree", "with": {} - }, - "folder_workspace_owner_type_unique": { - "name": "folder_workspace_owner_type_unique", - "columns": [ - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "workspace_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_project_owner_type_unique": { - "name": "folder_project_owner_type_unique", - "columns": [ - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "project_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": true, - "method": "btree", - "with": {} } }, "foreignKeys": { @@ -8917,7 +8863,18 @@ } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {}, + "uniqueConstraints": { + "folder_workspace_owner_type_unique": { + "name": "folder_workspace_owner_type_unique", + "nullsNotDistinct": false, + "columns": ["id", "workspace_id", "resource_type"] + }, + "folder_project_owner_type_unique": { + "name": "folder_project_owner_type_unique", + "nullsNotDistinct": false, + "columns": ["id", "project_id", "resource_type"] + } + }, "policies": {}, "checkConstraints": { "folder_owner_check": { diff --git a/packages/db/migrations/meta/0427_snapshot.json b/packages/db/migrations/meta/0427_snapshot.json index e424edfa83f..e9bdf2d7368 100644 --- a/packages/db/migrations/meta/0427_snapshot.json +++ b/packages/db/migrations/meta/0427_snapshot.json @@ -8804,60 +8804,6 @@ "concurrently": true, "method": "btree", "with": {} - }, - "folder_workspace_owner_type_unique": { - "name": "folder_workspace_owner_type_unique", - "columns": [ - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "workspace_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": true, - "method": "btree", - "with": {} - }, - "folder_project_owner_type_unique": { - "name": "folder_project_owner_type_unique", - "columns": [ - { - "expression": "id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "project_id", - "isExpression": false, - "asc": true, - "nulls": "last" - }, - { - "expression": "resource_type", - "isExpression": false, - "asc": true, - "nulls": "last" - } - ], - "isUnique": true, - "concurrently": true, - "method": "btree", - "with": {} } }, "foreignKeys": { @@ -8917,7 +8863,18 @@ } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {}, + "uniqueConstraints": { + "folder_workspace_owner_type_unique": { + "name": "folder_workspace_owner_type_unique", + "nullsNotDistinct": false, + "columns": ["id", "workspace_id", "resource_type"] + }, + "folder_project_owner_type_unique": { + "name": "folder_project_owner_type_unique", + "nullsNotDistinct": false, + "columns": ["id", "project_id", "resource_type"] + } + }, "policies": {}, "checkConstraints": { "folder_owner_check": { diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 25831cb9831..14b2b7f818a 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -277,17 +277,21 @@ export const folder = pgTable( 'folder_owner_check', sql`num_nonnulls(${table.workspaceId}, ${table.projectId}) = 1 AND (${table.projectId} IS NULL OR ${table.resourceType} = 'file')` ), - workspaceOwnerTypeUnique: uniqueIndex('folder_workspace_owner_type_unique') - .on(table.id, table.workspaceId, table.resourceType) - .concurrently(), + workspaceOwnerTypeUnique: unique('folder_workspace_owner_type_unique').on( + table.id, + table.workspaceId, + table.resourceType + ), workspaceParentOwnerFk: foreignKey({ name: 'folder_workspace_parent_owner_fk', columns: [table.parentId, table.workspaceId, table.resourceType], foreignColumns: [table.id, table.workspaceId, table.resourceType], }), - projectOwnerTypeUnique: uniqueIndex('folder_project_owner_type_unique') - .on(table.id, table.projectId, table.resourceType) - .concurrently(), + projectOwnerTypeUnique: unique('folder_project_owner_type_unique').on( + table.id, + table.projectId, + table.resourceType + ), projectParentOwnerFk: foreignKey({ name: 'folder_project_parent_owner_fk', columns: [table.parentId, table.projectId, table.resourceType], diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index cd67bfcb906..8ed8a528740 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -307,6 +307,29 @@ export const workflows = pgTable('workflow', { ]) }, 60_000) + it('preserves populated composite folder references when schema push is repeated', async () => { + await schema(`export const folders = pgTable('folder', { + id: text('id').primaryKey(), resourceType: text('resource_type').notNull(), + workspaceId: text('workspace_id').notNull(), parentId: text('parent_id'), +}, (t) => [ + unique('folder_workspace_owner_type_unique').on(t.id, t.workspaceId, t.resourceType), + foreignKey({ name: 'folder_workspace_parent_owner_fk', columns: [t.parentId, t.workspaceId, t.resourceType], foreignColumns: [t.id, t.workspaceId, t.resourceType] }), +])`) + const first = push() + expect(first.status, first.stdout + first.stderr).toBe(0) + await sql`INSERT INTO folder (id, workspace_id, resource_type, parent_id) + VALUES ('parent', 'workspace-a', 'file', NULL), ('child', 'workspace-a', 'file', 'parent')` + const repeated = push() + expect(repeated.status, repeated.stdout + repeated.stderr).toBe(0) + expect(await sql`SELECT id, parent_id, workspace_id FROM folder ORDER BY id`).toEqual([ + { id: 'child', parent_id: 'parent', workspace_id: 'workspace-a' }, + { id: 'parent', parent_id: null, workspace_id: 'workspace-a' }, + ]) + await expect( + sql`UPDATE folder SET workspace_id = 'workspace-b' WHERE id = 'child'` + ).rejects.toMatchObject({ code: '23503' }) + }, 30_000) + it('retires the legacy size bridge without losing bigint or unbackfilled values', async () => { await sql`CREATE TABLE workspace_files (id text PRIMARY KEY, size integer NOT NULL, size_bytes bigint)` await sql`INSERT INTO workspace_files VALUES ('legacy', 123, NULL), ('large', 2147483647, 5000000000)` diff --git a/patches/drizzle-kit@0.31.10.patch b/patches/drizzle-kit@0.31.10.patch index 5deebcc76a3..beafb2ebc78 100644 --- a/patches/drizzle-kit@0.31.10.patch +++ b/patches/drizzle-kit@0.31.10.patch @@ -2,6 +2,20 @@ diff --git a/bin.cjs b/bin.cjs index 07274ea6f53fd54a129b3d338892feda6e995cff..2c4556d9f8741560581e4c1f4e06d1dfb8dda63c 100755 --- a/bin.cjs +++ b/bin.cjs +@@ -17734,8 +17734,9 @@ + const tableConstraints = await db.query( + `SELECT c.column_name, c.data_type, constraint_type, constraint_name, constraint_schema + FROM information_schema.table_constraints tc +- JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name) +- JOIN information_schema.columns AS c ON c.table_schema = tc.constraint_schema +- AND tc.table_name = c.table_name AND ccu.column_name = c.column_name +- WHERE tc.table_name = '${tableName}' and constraint_schema = '${tableSchema}';` ++ JOIN information_schema.key_column_usage AS kcu USING (constraint_catalog, constraint_schema, constraint_name, table_catalog, table_schema, table_name) ++ JOIN information_schema.columns AS c ON c.table_schema = tc.table_schema ++ AND tc.table_name = c.table_name AND kcu.column_name = c.column_name ++ WHERE tc.table_name = '${tableName}' and constraint_schema = '${tableSchema}' ++ ORDER BY constraint_name, kcu.ordinal_position;` + ); @@ -32692,7 +32692,7 @@ var init_migrate = __esm({ }; }; From 42bb8becf14568ed6841e00e3300fec6bc527515 Mon Sep 17 00:00:00 2001 From: Marcus Chandra Date: Tue, 13 Oct 2026 23:30:17 -0700 Subject: [PATCH 113/113] fix(db): correct dependency patch offsets for clean installs --- packages/db/scripts/push.integration.ts | 2 +- patches/drizzle-kit@0.31.10.patch | 53 +++++-------------------- 2 files changed, 10 insertions(+), 45 deletions(-) diff --git a/packages/db/scripts/push.integration.ts b/packages/db/scripts/push.integration.ts index 8ed8a528740..576e1afc878 100644 --- a/packages/db/scripts/push.integration.ts +++ b/packages/db/scripts/push.integration.ts @@ -328,7 +328,7 @@ export const workflows = pgTable('workflow', { await expect( sql`UPDATE folder SET workspace_id = 'workspace-b' WHERE id = 'child'` ).rejects.toMatchObject({ code: '23503' }) - }, 30_000) + }, 60_000) it('retires the legacy size bridge without losing bigint or unbackfilled values', async () => { await sql`CREATE TABLE workspace_files (id text PRIMARY KEY, size integer NOT NULL, size_bytes bigint)` diff --git a/patches/drizzle-kit@0.31.10.patch b/patches/drizzle-kit@0.31.10.patch index beafb2ebc78..67c42f08929 100644 --- a/patches/drizzle-kit@0.31.10.patch +++ b/patches/drizzle-kit@0.31.10.patch @@ -1,10 +1,7 @@ diff --git a/bin.cjs b/bin.cjs -index 07274ea6f53fd54a129b3d338892feda6e995cff..2c4556d9f8741560581e4c1f4e06d1dfb8dda63c 100755 --- a/bin.cjs +++ b/bin.cjs -@@ -17734,8 +17734,9 @@ - const tableConstraints = await db.query( - `SELECT c.column_name, c.data_type, constraint_type, constraint_name, constraint_schema +@@ -17736,6 +17736,7 @@ FROM information_schema.table_constraints tc - JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name) - JOIN information_schema.columns AS c ON c.table_schema = tc.constraint_schema @@ -16,74 +13,42 @@ index 07274ea6f53fd54a129b3d338892feda6e995cff..2c4556d9f8741560581e4c1f4e06d1df + WHERE tc.table_name = '${tableName}' and constraint_schema = '${tableSchema}' + ORDER BY constraint_name, kcu.ordinal_position;` ); -@@ -32692,7 +32692,7 @@ var init_migrate = __esm({ - }; - }; +@@ -32694,3 +32695,3 @@ promptColumnsConflicts = async (tableName, newColumns, missingColumns) => { - if (newColumns.length === 0 || missingColumns.length === 0) { + if (process.env.SIM_DB_PUSH_RENAME_MODE === "create" || newColumns.length === 0 || missingColumns.length === 0) { return { created: newColumns, renamed: [], deleted: missingColumns }; - } - const result = { -@@ -32742,7 +32742,7 @@ var init_migrate = __esm({ - return result; - }; +@@ -32744,3 +32745,3 @@ promptNamedConflict = async (newItems, missingItems, entity) => { - if (missingItems.length === 0 || newItems.length === 0) { + if (process.env.SIM_DB_PUSH_RENAME_MODE === "create" || missingItems.length === 0 || newItems.length === 0) { return { - created: newItems, - renamed: [], -@@ -32792,7 +32792,7 @@ var init_migrate = __esm({ - return result; - }; +@@ -32794,3 +32795,3 @@ promptNamedWithSchemasConflict = async (newItems, missingItems, entity) => { - if (missingItems.length === 0 || newItems.length === 0) { + if (process.env.SIM_DB_PUSH_RENAME_MODE === "create" || missingItems.length === 0 || newItems.length === 0) { return { - created: newItems, - renamed: [], -@@ -32852,7 +32852,7 @@ var init_migrate = __esm({ - return result; - }; +@@ -32854,3 +32855,3 @@ promptSchemasConflict = async (newSchemas, missingSchemas) => { - if (missingSchemas.length === 0 || newSchemas.length === 0) { + if (process.env.SIM_DB_PUSH_RENAME_MODE === "create" || missingSchemas.length === 0 || newSchemas.length === 0) { return { created: newSchemas, renamed: [], deleted: missingSchemas }; - } - const result = { -@@ -82739,7 +82739,7 @@ var init_push = __esm({ - ); - if ((data == null ? void 0 : data.index) === 0) { +@@ -82741,3 +82742,3 @@ (0, import_hanji13.render)(`[${source_default.red("x")}] All changes were aborted`); - process.exit(0); + process.exit(1); } - } - } -@@ -82761,7 +82761,7 @@ var init_push = __esm({ - ); - if ((data == null ? void 0 : data.index) === 0) { +@@ -82763,3 +82764,3 @@ (0, import_hanji13.render)(`[${source_default.red("x")}] All changes were aborted`); - process.exit(0); + process.exit(1); } - } - for (const dStmnt of statementsToExecute) { -@@ -82774,7 +82774,7 @@ var init_push = __esm({ - } - } +@@ -82776,3 +82777,3 @@ } catch (e4) { - console.error(e4); + throw e4; } - }; - sqlitePush = async (schemaPath, verbose, strict, credentials2, tablesFilter, force, casing2) => { -@@ -92279,6 +92279,7 @@ var push = command2({ - } - } catch (e4) { +@@ -92281,2 +92282,3 @@ console.error(e4); + process.exit(1); } - process.exit(0); - }