0 ? Math.min(segments, Math.round((used / scale) * segments)) : 0
+ const allowedSegments = scale > 0 ? Math.round((total / scale) * segments) : 0
+
+ return (
+
+ {Array.from({ length: segments }).map((_, index) => {
+ const isFilled = index < filledSegments
+ const isOverage = index >= allowedSegments && isFilled
+ return (
+
+ )
+ })}
+
+ )
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/index.ts b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/index.ts
index fc31aad7ec5..62c4f87254e 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/index.ts
@@ -1,4 +1,3 @@
-export { ManageCreditsModal } from './manage-credits-modal'
export { NoOrganizationView } from './no-organization-view'
export { OrganizationMemberLists } from './organization-member-lists'
export { RemoveMemberDialog } from './remove-member-dialog'
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/organization-member-lists/organization-member-lists.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/organization-member-lists/organization-member-lists.tsx
index aae0637e267..cadb43c8967 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/organization-member-lists/organization-member-lists.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/organization-member-lists/organization-member-lists.tsx
@@ -19,6 +19,10 @@ import type {
RosterWorkspaceAccess,
} from '@/lib/api/contracts/organization'
import type { Member } from '@/lib/workspaces/organization'
+import {
+ ManageCreditsModal,
+ type ManageCreditsTarget,
+} from '@/app/workspace/[workspaceId]/settings/components/manage-credits-modal'
import {
MemberRow,
MemberSection,
@@ -27,10 +31,6 @@ import {
type RowAction,
RowActionsMenu,
} from '@/app/workspace/[workspaceId]/settings/components/row-actions-menu'
-import {
- ManageCreditsModal,
- type ManageCreditsTarget,
-} from '@/app/workspace/[workspaceId]/settings/components/team-management/components/manage-credits-modal'
import {
useRemoveWorkspaceMember,
useUpdateWorkspacePermissions,
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/team-seats-overview/team-seats-overview.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/team-seats-overview/team-seats-overview.tsx
index 8df81724bb1..66f7eac5162 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/team-seats-overview/team-seats-overview.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/team-seats-overview/team-seats-overview.tsx
@@ -1,5 +1,6 @@
-import { Badge, ChipLink, cn } from '@sim/emcn'
+import { Badge, ChipLink } from '@sim/emcn'
import { checkEnterprisePlan } from '@/lib/billing/subscriptions/utils'
+import { SegmentedMeter } from '@/app/workspace/[workspaceId]/settings/components/segmented-meter'
import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section'
type Subscription = {
@@ -107,25 +108,7 @@ export function TeamSeatsOverview({
-
- {Array.from({ length: pillCount }).map((_, i) => {
- const isFilled = i < usedSeats
- const isOverage = i >= totalSeats
- return (
-
- )
- })}
-
+
{isOverLimit
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/team-management.test.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/team-management.test.tsx
index 94c191fb855..ffa5a03c2ff 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/team-management.test.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/team-management.test.tsx
@@ -94,7 +94,11 @@ describe('TeamManagement organization errors', () => {
isLoading: false,
})
- act(() => root.render( ))
+ act(() =>
+ root.render(
+
+ )
+ )
expect(container.textContent).toContain('Organization request failed')
expect(container.textContent).not.toContain('no-organization-view')
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/team-management.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/team-management.tsx
index b62f1e0b292..8efc23f7ba5 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/team-management.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/team-management.tsx
@@ -35,13 +35,14 @@ const logger = createLogger('TeamManagement')
interface TeamManagementProps {
organizationId: string
- billingHref?: string
+ /**
+ * Required: organization billing is reached only through a workspace, so the
+ * caller — which knows the workspace — is the only thing that can build it.
+ */
+ billingHref: string
}
-export function TeamManagement({
- organizationId,
- billingHref = `/organization/${organizationId}/settings/billing`,
-}: TeamManagementProps) {
+export function TeamManagement({ organizationId, billingHref }: TeamManagementProps) {
const { data: session } = useSession()
const { isInvitationsDisabled } = usePermissionConfig()
const [memberQuery, setMemberQuery] = useSettingsSearch()
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/navigation.test.ts b/apps/sim/app/workspace/[workspaceId]/settings/navigation.test.ts
index b3c64e62c0d..f304c1e6574 100644
--- a/apps/sim/app/workspace/[workspaceId]/settings/navigation.test.ts
+++ b/apps/sim/app/workspace/[workspaceId]/settings/navigation.test.ts
@@ -31,6 +31,7 @@ describe('unified settings navigation', () => {
{ id: 'billing', label: 'Subscription', section: 'account' },
{ id: 'teammates', label: 'Teammates', section: 'workspace' },
{ id: 'organization', label: 'Members', section: 'organization' },
+ { id: 'usage', label: 'Usage tracking', section: 'organization' },
{ id: 'secrets', label: 'Secrets', section: 'workspace' },
{ id: 'credential-groups', label: 'Credential groups', section: 'workspace' },
{ id: 'custom-tools', label: 'Custom tools', section: 'workspace' },
@@ -82,6 +83,7 @@ describe('unified settings navigation', () => {
])
expect(idsForSection('organization')).toEqual([
'organization',
+ 'usage',
'custom-blocks',
'forks',
'access-control',
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/usage/events/layout.tsx b/apps/sim/app/workspace/[workspaceId]/settings/usage/events/layout.tsx
new file mode 100644
index 00000000000..5886a0c78b5
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/settings/usage/events/layout.tsx
@@ -0,0 +1,18 @@
+import {
+ SettingsHeaderProvider,
+ SettingsHeaderShell,
+} from '@/app/workspace/[workspaceId]/settings/components/settings-header/settings-header'
+
+/**
+ * Usage events is a static route outside `[section]`, so it inherits none of
+ * `SettingsSectionLayout`'s chrome. Its body renders through `SettingsPanel`, which
+ * only registers header config into `SettingsHeaderProvider` — without this shell the
+ * page would have no header bar, title, or scroll region.
+ */
+export default function UsageEventsLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/usage/events/loading.tsx b/apps/sim/app/workspace/[workspaceId]/settings/usage/events/loading.tsx
new file mode 100644
index 00000000000..aa745a1024e
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/settings/usage/events/loading.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+import { ArrowLeft } from '@sim/emcn/icons'
+import { useParams, useRouter } from 'next/navigation'
+import { SettingsPanel } from '@/app/workspace/[workspaceId]/settings/components/settings-panel'
+
+export default function UsageEventsLoading() {
+ const router = useRouter()
+ const { workspaceId } = useParams<{ workspaceId: string }>()
+
+ return (
+ router.push(`/workspace/${workspaceId}/settings/usage`),
+ }}
+ title='Usage events'
+ description='Every credit-consuming event behind your usage.'
+ />
+ )
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/settings/usage/events/page.tsx b/apps/sim/app/workspace/[workspaceId]/settings/usage/events/page.tsx
new file mode 100644
index 00000000000..36c0777ef07
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/settings/usage/events/page.tsx
@@ -0,0 +1,47 @@
+import { Suspense } from 'react'
+import type { Metadata } from 'next'
+import { redirect } from 'next/navigation'
+import { getSession } from '@/lib/auth'
+import { isOrganizationFeatureEntitled } from '@/lib/billing/core/subscription'
+import { isUsageMonitoringEnabled } from '@/lib/core/config/env-flags'
+import { getWorkspaceHostContextForViewer } from '@/lib/workspaces/host-context'
+import UsageEventsLoading from '@/app/workspace/[workspaceId]/settings/usage/events/loading'
+import { UsageEventsView } from '@/ee/organization-usage/components/usage-events-view'
+
+export const metadata: Metadata = {
+ title: 'Usage events',
+}
+
+interface UsageEventsPageProps {
+ params: Promise<{ workspaceId: string }>
+}
+
+/**
+ * This route sits outside `[section]`, so it inherits none of that page's gates and
+ * has to repeat them: a host organization, an org-admin viewer, and the enterprise
+ * entitlement. The API refuses regardless; this keeps a deep link from rendering a
+ * shell the viewer can never fill.
+ */
+export default async function UsageEventsPage({ params }: UsageEventsPageProps) {
+ const session = await getSession()
+ if (!session?.user) redirect('/login')
+
+ const { workspaceId } = await params
+ const hostContext = await getWorkspaceHostContextForViewer(workspaceId, session.user.id)
+ const organizationId = hostContext?.hostOrganizationId
+ if (!hostContext || !organizationId || !hostContext.viewer.isHostOrganizationAdmin) {
+ redirect(`/workspace/${workspaceId}/settings/general`)
+ }
+ if (!(await isOrganizationFeatureEntitled(organizationId, isUsageMonitoringEnabled))) {
+ redirect(`/workspace/${workspaceId}/settings/general`)
+ }
+
+ return (
+ }>
+
+
+ )
+}
diff --git a/apps/sim/components/charts/bar-chart.tsx b/apps/sim/components/charts/bar-chart.tsx
new file mode 100644
index 00000000000..8a40747c18b
--- /dev/null
+++ b/apps/sim/components/charts/bar-chart.tsx
@@ -0,0 +1,373 @@
+'use client'
+
+import { memo, useId, useMemo, useState } from 'react'
+import { cn } from '@sim/emcn'
+import {
+ formatChartCompactNumber,
+ formatChartLatency,
+ formatChartTimestamp,
+} from '@/components/charts/chart-format'
+import {
+ CHART_DEFAULT_HEIGHT,
+ CHART_GRID_FRACTIONS,
+ CHART_PADDING,
+ CHART_TICK_FILL,
+ CHART_TICK_FONT_SIZE,
+ chartPlotBand,
+ formatTimeTick,
+ resolveSpanMs,
+ resolveTimeTickIndices,
+} from '@/components/charts/chart-geometry'
+import {
+ ChartTooltip,
+ ChartTooltipRow,
+ estimateTooltipWidth,
+ positionChartTooltip,
+} from '@/components/charts/chart-tooltip'
+import {
+ useChartWidth,
+ useIsDarkTheme,
+ useResolvedChartColors,
+} from '@/components/charts/use-chart-theme'
+
+export interface BarChartPoint {
+ timestamp: string
+ value: number
+}
+
+interface BarChartProps {
+ data: BarChartPoint[]
+ /** Pass `''` for the caller-owned-wrapper form, mirroring {@link LineChart}. */
+ label: string
+ color: string
+ /** `''` | `'%'` | `'ms'` | `'latency'` | `'credits'` — drives tick and tooltip formatting. */
+ unit?: string
+ height?: number
+ /** Bucket drawn at full opacity, e.g. the period in progress. */
+ highlightIndex?: number
+}
+
+/**
+ * Discrete time buckets as bars.
+ *
+ * The sibling of {@link LineChart}, and deliberately built from the same geometry,
+ * tooltip, and theme modules: a smoothed line implies a continuous signal between
+ * samples, which is wrong for a calendar bucket like a day's spend, but the two must
+ * still line up pixel-for-pixel when stacked in one card.
+ */
+function BarChartComponent({
+ data,
+ label,
+ color,
+ unit,
+ height = CHART_DEFAULT_HEIGHT,
+ highlightIndex,
+}: BarChartProps) {
+ /*
+ `useId`, not `useRef(generateShortId())`: a ref initializer is evaluated on
+ every render and all but the first result thrown away, and React already has
+ a hook whose whole job is a stable unique id.
+ */
+ const uniqueId = useId().replace(/:/g, '')
+ const [containerRef, containerWidth] = useChartWidth()
+ const width = containerWidth ?? 0
+ const padding = CHART_PADDING
+ const chartWidth = width - padding.left - padding.right
+ const chartHeight = height - padding.top - padding.bottom
+ const { yMin, yMax } = chartPlotBand(height)
+ const isDark = useIsDarkTheme()
+ const [hoverIndex, setHoverIndex] = useState(null)
+ const [hoverPos, setHoverPos] = useState<{ x: number; y: number } | null>(null)
+
+ const colorTokens = useMemo(() => ({ base: color }), [color])
+ const resolvedColors = useResolvedChartColors(colorTokens)
+ const resolvedColor = resolvedColors.base || color
+
+ const hasExternalWrapper = !label
+
+ /**
+ * The track is read against its own background, so its opacity is per-theme
+ * rather than one shared value. `--border` is the platform's neutral track
+ * token — the same one the proportional row meters use — but it resolves to
+ * `#444` on dark and `#d8d8d8` on light, and a strength that reads as a column
+ * on near-black is a half-percent delta on white. Hover keeps the same ratio.
+ */
+ const trackOpacity = isDark ? 0.12 : 0.3
+ const trackHoverOpacity = isDark ? 0.22 : 0.5
+
+ const maxValue = useMemo(() => {
+ const peak = Math.max(...data.map((d) => d.value), 0)
+ return peak <= 0 ? 1 : peak * 1.1
+ }, [data])
+
+ /** Slot geometry: every bucket owns an equal slice, with the bar centred in it. */
+ const slot = data.length > 0 ? Math.max(1, chartWidth) / data.length : 0
+ const barWidth = Math.max(1, Math.min(24, slot * 0.7))
+
+ const bars = useMemo(
+ () =>
+ data.map((point, index) => {
+ const x = padding.left + slot * index + (slot - barWidth) / 2
+ const rawY = padding.top + chartHeight - (point.value / maxValue) * chartHeight
+ const y = Math.max(yMin, Math.min(yMax, rawY))
+ return {
+ x,
+ y,
+ /*
+ * A zero bucket draws nothing. The clamp above keeps a *drawn* bar off the
+ * axis rule, but applied to zero it floored the bar at the 3px band and
+ * every empty day rendered as a small amount of usage — the densified zeros
+ * this chart exists to show honestly. Only the track represents an empty
+ * bucket.
+ */
+ height: point.value > 0 ? Math.max(0, height - padding.bottom - y) : 0,
+ point,
+ }
+ }),
+ [data, slot, barWidth, maxValue, chartHeight, height, padding.left, padding.top, yMin, yMax]
+ )
+
+ const formatValue = (value?: number) => {
+ if (typeof value !== 'number' || !Number.isFinite(value)) return '—'
+ const suffix = (unit ?? '').toLowerCase()
+ if (suffix.includes('%')) return `${value.toFixed(1)}%`
+ if (suffix === 'latency') return formatChartLatency(value)
+ if (suffix.includes('ms')) return `${Math.round(value)}ms`
+ if (suffix === 'credits') return formatChartCompactNumber(value)
+ return `${Math.round(value)}${unit ?? ''}`
+ }
+
+ if (containerWidth === null) {
+ return (
+
+ )
+ }
+
+ if (data.length === 0) {
+ return (
+ // Keeps the measurement ref: dropping it here left the observer watching a
+ // detached node, so a resize while empty was never seen and the next non-empty
+ // render laid out at the stale width.
+
+ )
+ }
+
+ const spanMs = resolveSpanMs(data)
+ const tickIndices = resolveTimeTickIndices(data.length, Math.max(1, chartWidth))
+
+ return (
+
+ {!hasExternalWrapper && (
+
+
{label}
+
+ )}
+
+ {
+ if (bars.length === 0 || slot <= 0) return
+ const rect = (e.currentTarget as SVGSVGElement).getBoundingClientRect()
+ const x = e.clientX - rect.left
+ // Bars own a slot, so the hovered bucket is which slot the cursor is in —
+ // not the nearest sample, which is how a line chart resolves it.
+ const index = Math.max(
+ 0,
+ Math.min(data.length - 1, Math.floor((x - padding.left) / slot))
+ )
+ setHoverIndex(index)
+ setHoverPos({ x, y: e.clientY - rect.top })
+ }}
+ onMouseLeave={() => {
+ setHoverIndex(null)
+ setHoverPos(null)
+ }}
+ >
+
+
+
+
+
+
+
+
+
+ {CHART_GRID_FRACTIONS.map((fraction) => (
+
+ ))}
+
+ {/*
+ A full-height track keeps an empty bucket visible and gives every slot
+ the same hover target, so a run of zero days reads as zero rather than
+ as missing data.
+
+ Drawn outside the blend group below: the bars want `screen` on dark so
+ the gradient stays luminous, but a track composited that way is only
+ legible against a dark background, and on white it disappears.
+ */}
+
+ {bars.map((bar, index) => (
+
+ ))}
+
+
+
+ {bars.map(
+ (bar, index) =>
+ bar.height > 0 && (
+
+ )
+ )}
+
+
+ {tickIndices.map((index) => {
+ const timestamp = data[index]?.timestamp
+ if (!timestamp) return null
+ const date = new Date(timestamp)
+ return (
+
+ {Number.isNaN(date.getTime()) ? '' : formatTimeTick(date, spanMs)}
+
+ )
+ })}
+
+
+ {/* Same formatter the tooltip uses, or the axis and the hover disagree
+ about what the numbers mean on any non-`credits` unit. */}
+ {formatValue(maxValue)}
+
+
+ 0
+
+
+
+
+
+ {hoverIndex !== null &&
+ bars[hoverIndex] &&
+ (() => {
+ const bar = bars[hoverIndex]
+ const value = formatValue(bar.point.value)
+ const { left, top } = positionChartTooltip({
+ anchorX: hoverPos?.x ?? bar.x,
+ anchorY: hoverPos?.y ?? bar.y,
+ width,
+ height,
+ tooltipMaxWidth: estimateTooltipWidth(value.length),
+ })
+ return (
+
+
+
+ )
+ })()}
+
+
+ )
+}
+
+export const BarChart = memo(BarChartComponent)
diff --git a/apps/sim/components/charts/chart-format.ts b/apps/sim/components/charts/chart-format.ts
new file mode 100644
index 00000000000..c5b7151814b
--- /dev/null
+++ b/apps/sim/components/charts/chart-format.ts
@@ -0,0 +1,31 @@
+import { formatDuration } from '@sim/utils/formatting'
+import { format } from 'date-fns'
+
+/**
+ * Value and tick formatting shared by the chart family.
+ *
+ * These live here rather than in the logs feature's `utils.ts` because that module
+ * imports the block registry, and a chart that reached for it would drag the whole
+ * executable registry into every consumer's bundle.
+ */
+
+/** Duration for an axis tick or tooltip. `—` for a missing or non-positive value. */
+export function formatChartLatency(ms: number): string {
+ if (!Number.isFinite(ms) || ms <= 0) return '—'
+ return formatDuration(ms, { precision: 2 }) ?? '—'
+}
+
+/** The tooltip's header line: `MAR 4 3:05 PM`. Empty for an unparseable timestamp. */
+export function formatChartTimestamp(timestamp?: string): string {
+ if (!timestamp) return ''
+ const date = new Date(timestamp)
+ if (Number.isNaN(date.getTime())) return ''
+ return `${format(date, 'MMM d').toUpperCase()} ${format(date, 'h:mm a')}`
+}
+
+/** Compact axis magnitude — `1.2k`, `3.4m`. */
+export function formatChartCompactNumber(value: number): string {
+ return new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 })
+ .format(value)
+ .toLowerCase()
+}
diff --git a/apps/sim/components/charts/chart-geometry.test.ts b/apps/sim/components/charts/chart-geometry.test.ts
new file mode 100644
index 00000000000..82c2694abbe
--- /dev/null
+++ b/apps/sim/components/charts/chart-geometry.test.ts
@@ -0,0 +1,80 @@
+/**
+ * @vitest-environment node
+ */
+import { describe, expect, it } from 'vitest'
+import {
+ CHART_PADDING,
+ chartPlotBand,
+ formatTimeTick,
+ resolveSpanMs,
+ resolveTimeTickIndices,
+} from '@/components/charts/chart-geometry'
+
+describe('resolveTimeTickIndices', () => {
+ it('budgets roughly one tick per 64px, clamped to 3..8', () => {
+ expect(resolveTimeTickIndices(100, 100).length).toBe(3)
+ expect(resolveTimeTickIndices(100, 320).length).toBe(5)
+ expect(resolveTimeTickIndices(100, 4000).length).toBe(8)
+ })
+
+ it('dedupes the collisions rounding produces on a short series', () => {
+ // 2 points across a wide chart wants 8 ticks but only has indices 0 and 1.
+ const indices = resolveTimeTickIndices(2, 4000)
+ expect(indices).toEqual([...new Set(indices)])
+ expect(indices.every((index) => index >= 0 && index < 2)).toBe(true)
+ })
+
+ it('always spans the full series, first index to last', () => {
+ const indices = resolveTimeTickIndices(50, 640)
+ expect(indices[0]).toBe(0)
+ expect(indices[indices.length - 1]).toBe(49)
+ })
+})
+
+describe('formatTimeTick', () => {
+ const date = new Date('2026-03-04T15:05:00.000Z')
+
+ it('shows clock time within a day and a half', () => {
+ expect(formatTimeTick(date, 36 * 60 * 60 * 1000)).toMatch(/^\d{2}:\d{2}$/)
+ })
+
+ it('shows a calendar day within a quarter', () => {
+ expect(formatTimeTick(date, 40 * 24 * 60 * 60 * 1000)).toMatch(/^[A-Z][a-z]{2} \d{1,2}$/)
+ })
+
+ it('shows month and year beyond a quarter', () => {
+ expect(formatTimeTick(date, 400 * 24 * 60 * 60 * 1000)).toMatch(/^[A-Z][a-z]{2} \d{4}$/)
+ })
+})
+
+describe('resolveSpanMs', () => {
+ it('measures first to last', () => {
+ expect(
+ resolveSpanMs([
+ { timestamp: '2026-03-04T00:00:00.000Z' },
+ { timestamp: '2026-03-05T00:00:00.000Z' },
+ ])
+ ).toBe(24 * 60 * 60 * 1000)
+ })
+
+ it('returns 0 for a degenerate or unparseable series rather than NaN', () => {
+ expect(resolveSpanMs([])).toBe(0)
+ expect(resolveSpanMs([{ timestamp: '2026-03-04T00:00:00.000Z' }])).toBe(0)
+ expect(resolveSpanMs([{ timestamp: 'nope' }, { timestamp: 'also nope' }])).toBe(0)
+ })
+})
+
+describe('chartPlotBand', () => {
+ it('insets the band so strokes clear the axis rules', () => {
+ // The line and bar charts both clamp to this band, which is what keeps them
+ // aligned when stacked in the same card.
+ expect(chartPlotBand(166)).toEqual({
+ yMin: CHART_PADDING.top + 3,
+ yMax: CHART_PADDING.top + (166 - CHART_PADDING.top - CHART_PADDING.bottom) - 3,
+ })
+ })
+
+ it('tracks a caller-supplied height', () => {
+ expect(chartPlotBand(240).yMax).toBeGreaterThan(chartPlotBand(166).yMax)
+ })
+})
diff --git a/apps/sim/components/charts/chart-geometry.ts b/apps/sim/components/charts/chart-geometry.ts
new file mode 100644
index 00000000000..e85a010d251
--- /dev/null
+++ b/apps/sim/components/charts/chart-geometry.ts
@@ -0,0 +1,67 @@
+/**
+ * Geometry shared by every chart in the family.
+ *
+ * Extracted so a sibling chart cannot drift: bar and line charts read the same
+ * padding, the same clamps, the same gridlines, and resolve their x ticks the same
+ * way, so two charts stacked in one card line up on the pixel.
+ *
+ * Pure — no React, no DOM — so a server module can read the constants.
+ */
+
+export const CHART_PADDING = { top: 16, right: 28, bottom: 26, left: 26 } as const
+
+/** Matches the loader placeholders callers size themselves against. */
+export const CHART_DEFAULT_HEIGHT = 166
+
+/** Below this the axis labels collide, so the chart scrolls rather than compresses. */
+export const CHART_MIN_WIDTH = 280
+
+export const CHART_TICK_FILL = 'var(--text-tertiary)'
+export const CHART_TICK_FONT_SIZE = '9'
+export const CHART_GRID_FRACTIONS = [0.25, 0.5, 0.75] as const
+
+/** Vertical clamp for plotted geometry, keeping strokes off the axis rules. */
+export function chartPlotBand(height: number): { yMin: number; yMax: number } {
+ const chartHeight = height - CHART_PADDING.top - CHART_PADDING.bottom
+ return { yMin: CHART_PADDING.top + 3, yMax: CHART_PADDING.top + chartHeight - 3 }
+}
+
+/**
+ * Evenly spaced point indices to label, budgeting ~64px per tick and deduping the
+ * collisions that rounding produces on short series.
+ */
+export function resolveTimeTickIndices(pointCount: number, usableWidth: number): number[] {
+ const approxLabelWidth = 64
+ const desired = Math.min(8, Math.max(3, Math.floor(usableWidth / approxLabelWidth)))
+ const seen = new Set()
+ return Array.from({ length: desired }, (_, i) =>
+ Math.round((i * (pointCount - 1)) / Math.max(1, desired - 1))
+ ).filter((index) => {
+ if (seen.has(index)) return false
+ seen.add(index)
+ return true
+ })
+}
+
+/**
+ * Tick label whose precision follows the window: clock time within a day and a half,
+ * calendar day within a quarter, month beyond that.
+ */
+export function formatTimeTick(date: Date, spanMs: number): string {
+ if (spanMs <= 36 * 60 * 60 * 1000) {
+ return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false })
+ }
+ if (spanMs <= 90 * 24 * 60 * 60 * 1000) {
+ return date.toLocaleString('en-US', { month: 'short', day: 'numeric' })
+ }
+ return date.toLocaleString('en-US', { month: 'short', year: 'numeric' })
+}
+
+/** Milliseconds between the first and last timestamp, or 0 for a degenerate series. */
+export function resolveSpanMs(points: ReadonlyArray<{ timestamp: string }>): number {
+ if (points.length < 2) return 0
+ const first = new Date(points[0].timestamp).getTime()
+ const last = new Date(points[points.length - 1].timestamp).getTime()
+ if (Number.isNaN(first) || Number.isNaN(last)) return 0
+ return Math.abs(last - first)
+}
diff --git a/apps/sim/components/charts/chart-tooltip.tsx b/apps/sim/components/charts/chart-tooltip.tsx
new file mode 100644
index 00000000000..6a49032394a
--- /dev/null
+++ b/apps/sim/components/charts/chart-tooltip.tsx
@@ -0,0 +1,89 @@
+'use client'
+
+import type { ReactNode } from 'react'
+import { CHART_PADDING } from '@/components/charts/chart-geometry'
+
+/**
+ * The chart family's hover surface. Defined once so a sibling chart cannot ship a
+ * tooltip that looks almost the same — this class string was previously duplicated
+ * between the line chart and the status bar.
+ */
+export const CHART_TOOLTIP_CLASSES =
+ 'pointer-events-none absolute rounded-lg border border-[var(--border-1)] bg-[var(--surface-1)] px-2 py-1.5 text-xs shadow-lg'
+
+interface PositionChartTooltipArgs {
+ anchorX: number
+ anchorY: number
+ width: number
+ height: number
+ tooltipMaxWidth: number
+}
+
+/**
+ * Places the tooltip beside the cursor, preferring the right and flipping left when
+ * it would overflow, then clamping into the plot band so it never escapes the card.
+ */
+export function positionChartTooltip({
+ anchorX,
+ anchorY,
+ width,
+ height,
+ tooltipMaxWidth,
+}: PositionChartTooltipArgs): { left: number; top: number } {
+ const margin = 10
+ const rightEdge = width - CHART_PADDING.right
+ const preferRight = anchorX + margin + tooltipMaxWidth <= rightEdge
+ const left = preferRight
+ ? Math.max(CHART_PADDING.left, Math.min(anchorX + margin, rightEdge - tooltipMaxWidth))
+ : Math.max(
+ CHART_PADDING.left,
+ Math.min(anchorX - margin - tooltipMaxWidth, rightEdge - tooltipMaxWidth)
+ )
+ const top = Math.min(
+ Math.max(anchorY - 26, CHART_PADDING.top),
+ height - CHART_PADDING.bottom - 18
+ )
+ return { left, top }
+}
+
+/** Width estimate for the longest `label value` row, used by {@link positionChartTooltip}. */
+export function estimateTooltipWidth(longestRowLength: number): number {
+ return Math.min(220, Math.max(80, 7 * longestRowLength + 24))
+}
+
+interface ChartTooltipProps {
+ left: number
+ top: number
+ /** Header line; omitted when the timestamp could not be formatted. */
+ date?: string
+ children: ReactNode
+}
+
+export function ChartTooltip({ left, top, date, children }: ChartTooltipProps) {
+ return (
+
+ {date &&
{date}
}
+ {children}
+
+ )
+}
+
+interface ChartTooltipRowProps {
+ color: string
+ label?: string
+ value: string
+}
+
+export function ChartTooltipRow({ color, label, value }: ChartTooltipRowProps) {
+ return (
+
+
+ {label && {label} }
+ {value}
+
+ )
+}
diff --git a/apps/sim/components/charts/index.ts b/apps/sim/components/charts/index.ts
new file mode 100644
index 00000000000..138241f0436
--- /dev/null
+++ b/apps/sim/components/charts/index.ts
@@ -0,0 +1,12 @@
+export { BarChart, type BarChartPoint } from '@/components/charts/bar-chart'
+export {
+ formatChartCompactNumber,
+ formatChartLatency,
+ formatChartTimestamp,
+} from '@/components/charts/chart-format'
+export { CHART_DEFAULT_HEIGHT } from '@/components/charts/chart-geometry'
+export {
+ LineChart,
+ type LineChartMultiSeries,
+ type LineChartPoint,
+} from '@/components/charts/line-chart'
diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/line-chart/line-chart.tsx b/apps/sim/components/charts/line-chart.tsx
similarity index 74%
rename from apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/line-chart/line-chart.tsx
rename to apps/sim/components/charts/line-chart.tsx
index 9717ab254e7..6cfd8d43979 100644
--- a/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/line-chart/line-chart.tsx
+++ b/apps/sim/components/charts/line-chart.tsx
@@ -1,7 +1,34 @@
-import { memo, useEffect, useMemo, useRef, useState } from 'react'
+'use client'
+
+import { memo, useId, useMemo, useState } from 'react'
import { Button, cn } from '@sim/emcn'
-import { generateShortId } from '@sim/utils/id'
-import { formatDate, formatLatency } from '@/app/workspace/[workspaceId]/logs/utils'
+import {
+ formatChartCompactNumber,
+ formatChartLatency,
+ formatChartTimestamp,
+} from '@/components/charts/chart-format'
+import {
+ CHART_DEFAULT_HEIGHT,
+ CHART_GRID_FRACTIONS,
+ CHART_PADDING,
+ CHART_TICK_FILL,
+ CHART_TICK_FONT_SIZE,
+ chartPlotBand,
+ formatTimeTick,
+ resolveSpanMs,
+ resolveTimeTickIndices,
+} from '@/components/charts/chart-geometry'
+import {
+ ChartTooltip,
+ ChartTooltipRow,
+ estimateTooltipWidth,
+ positionChartTooltip,
+} from '@/components/charts/chart-tooltip'
+import {
+ useChartWidth,
+ useIsDarkTheme,
+ useResolvedChartColors,
+} from '@/components/charts/use-chart-theme'
export interface LineChartPoint {
timestamp: string
@@ -16,83 +43,50 @@ export interface LineChartMultiSeries {
dashed?: boolean
}
+interface LineChartProps {
+ data: LineChartPoint[]
+ /** Pass `''` for the caller-owned-wrapper form: no card chrome, title, or legend. */
+ label: string
+ color: string
+ unit?: string
+ series?: LineChartMultiSeries[]
+ height?: number
+}
+
function LineChartComponent({
data,
label,
color,
unit,
series,
-}: {
- data: LineChartPoint[]
- label: string
- color: string
- unit?: string
- series?: LineChartMultiSeries[]
-}) {
- const containerRef = useRef(null)
- const uniqueId = useRef(`chart-${generateShortId(7)}`).current
- const [containerWidth, setContainerWidth] = useState(null)
+ height = CHART_DEFAULT_HEIGHT,
+}: LineChartProps) {
+ /*
+ `useId`, not `useRef(generateShortId())`: a ref initializer is evaluated on
+ every render and all but the first result thrown away, and React already has
+ a hook whose whole job is a stable unique id.
+ */
+ const uniqueId = useId().replace(/:/g, '')
+ const [containerRef, containerWidth] = useChartWidth()
const width = containerWidth ?? 0
- const height = 166
- const padding = { top: 16, right: 28, bottom: 26, left: 26 }
- useEffect(() => {
- if (!containerRef.current) return
- const element = containerRef.current
- const ro = new ResizeObserver((entries) => {
- const entry = entries[0]
- if (entry?.contentRect && entry.contentRect.width > 0) {
- const w = Math.max(280, Math.floor(entry.contentRect.width))
- setContainerWidth(w)
- }
- })
- ro.observe(element)
- const rect = element.getBoundingClientRect()
- if (rect?.width && rect.width > 0) setContainerWidth(Math.max(280, Math.floor(rect.width)))
- return () => ro.disconnect()
- }, [])
+ const padding = CHART_PADDING
const chartWidth = width - padding.left - padding.right
const chartHeight = height - padding.top - padding.bottom
const [hoverIndex, setHoverIndex] = useState(null)
- const [isDark, setIsDark] = useState(true)
+ const isDark = useIsDarkTheme()
const [hoverSeriesId, setHoverSeriesId] = useState(null)
const [activeSeriesId, setActiveSeriesId] = useState(null)
const [hoverPos, setHoverPos] = useState<{ x: number; y: number } | null>(null)
- const [resolvedColors, setResolvedColors] = useState>({})
-
- useEffect(() => {
- if (typeof window === 'undefined') return
- const el = document.documentElement
- const update = () => setIsDark(el.classList.contains('dark'))
- update()
- const observer = new MutationObserver(update)
- observer.observe(el, { attributes: true, attributeFilter: ['class'] })
- return () => observer.disconnect()
- }, [])
-
- useEffect(() => {
- if (typeof window === 'undefined') return
-
- const resolveColor = (c: string): string => {
- if (!c.startsWith('var(')) return c
-
- const tempEl = document.createElement('div')
- tempEl.style.color = c
- document.body.appendChild(tempEl)
- const computed = window.getComputedStyle(tempEl).color
- document.body.removeChild(tempEl)
- return computed
- }
-
- const colorMap: Record = { base: resolveColor(color) }
- const allSeriesToResolve = Array.isArray(series) && series.length > 0 ? series : []
- for (const s of allSeriesToResolve) {
+ const colorTokens = useMemo(() => {
+ const tokens: Record = { base: color }
+ for (const s of series ?? []) {
const id = s.id || s.label || ''
- if (id) colorMap[id] = resolveColor(s.color)
+ if (id) tokens[id] = s.color
}
-
- setResolvedColors(colorMap)
+ return tokens
}, [color, series])
+ const resolvedColors = useResolvedChartColors(colorTokens)
const hasExternalWrapper = !label || label === ''
@@ -135,8 +129,7 @@ function LineChartComponent({
}
}, [allSeries, unit])
- const yMin = padding.top + 3
- const yMax = padding.top + chartHeight - 3
+ const { yMin, yMax } = chartPlotBand(height)
const scaledPoints = useMemo(
() =>
@@ -202,26 +195,8 @@ function LineChartComponent({
return d
}, [scaledPoints, yMin, yMax])
- const getCompactDateLabel = (timestamp?: string) => {
- if (!timestamp) return ''
- try {
- const f = formatDate(timestamp)
- return `${f.compactDate} ${f.compactTime}`
- } catch (e) {
- const d = new Date(timestamp)
- if (Number.isNaN(d.getTime())) return ''
- return d.toLocaleString('en-US', {
- month: 'short',
- day: 'numeric',
- hour: '2-digit',
- minute: '2-digit',
- hour12: false,
- })
- }
- }
-
const currentHoverDate =
- hoverIndex !== null && data[hoverIndex] ? getCompactDateLabel(data[hoverIndex].timestamp) : ''
+ hoverIndex !== null && data[hoverIndex] ? formatChartTimestamp(data[hoverIndex].timestamp) : ''
if (containerWidth === null) {
return (
@@ -237,10 +212,16 @@ function LineChartComponent({
return (
@@ -251,7 +232,14 @@ function LineChartComponent({
@@ -371,7 +359,7 @@ function LineChartComponent({
strokeWidth='1'
/>
- {[0.25, 0.5, 0.75].map((p) => (
+ {CHART_GRID_FRACTIONS.map((p) => (
{
if (data.length < 2) return null
const usableW = Math.max(1, chartWidth)
- const firstTs = new Date(data[0].timestamp)
- const lastTs = new Date(data[data.length - 1].timestamp)
- const spanMs = Math.abs(lastTs.getTime() - firstTs.getTime())
-
- const approxLabelWidth = 64
- const desired = Math.min(8, Math.max(3, Math.floor(usableW / approxLabelWidth)))
- const rawIdx = Array.from({ length: desired }, (_, i) =>
- Math.round((i * (data.length - 1)) / Math.max(1, desired - 1))
- )
- const seen = new Set()
- const idx = rawIdx.filter((i) => {
- if (seen.has(i)) return false
- seen.add(i)
- return true
- })
-
- const formatTick = (d: Date) => {
- if (spanMs <= 36 * 60 * 60 * 1000) {
- return d.toLocaleTimeString('en-US', {
- hour: '2-digit',
- minute: '2-digit',
- hour12: false,
- })
- }
- if (spanMs <= 90 * 24 * 60 * 60 * 1000) {
- return d.toLocaleString('en-US', { month: 'short', day: 'numeric' })
- }
- return d.toLocaleString('en-US', { month: 'short', year: 'numeric' })
- }
+ const spanMs = resolveSpanMs(data)
+ const idx = resolveTimeTickIndices(data.length, usableW)
return idx.map((i) => {
const x = padding.left + (i / (data.length - 1 || 1)) * usableW
const tsSource = data[i]?.timestamp
if (!tsSource) return null
const ts = new Date(tsSource)
- const labelStr = Number.isNaN(ts.getTime()) ? '' : formatTick(ts)
+ const labelStr = Number.isNaN(ts.getTime()) ? '' : formatTimeTick(ts, spanMs)
return (
{labelStr}
@@ -577,16 +538,8 @@ function LineChartComponent({
const showInTicks = unitSuffix === '%'
const isLatency = unitSuffix.toLowerCase() === 'latency'
const fmtCompact = (v: number) => {
- if (isLatency) {
- if (v === 0) return '0'
- return formatLatency(v)
- }
- return new Intl.NumberFormat('en-US', {
- notation: 'compact',
- maximumFractionDigits: 1,
- })
- .format(v)
- .toLowerCase()
+ if (isLatency) return v === 0 ? '0' : formatChartLatency(v)
+ return formatChartCompactNumber(v)
}
return (
<>
@@ -594,8 +547,8 @@ function LineChartComponent({
x={padding.left - 8}
y={padding.top}
textAnchor='end'
- fontSize='9'
- fill='var(--text-tertiary)'
+ fontSize={CHART_TICK_FONT_SIZE}
+ fill={CHART_TICK_FILL}
>
{fmtCompact(maxValue)}
{showInTicks && !isLatency ? unit : ''}
@@ -604,8 +557,8 @@ function LineChartComponent({
x={padding.left - 8}
y={height - padding.bottom}
textAnchor='end'
- fontSize='9'
- fill='var(--text-tertiary)'
+ fontSize={CHART_TICK_FONT_SIZE}
+ fill={CHART_TICK_FILL}
>
{fmtCompact(minValue)}
{showInTicks && !isLatency ? unit : ''}
@@ -640,7 +593,7 @@ function LineChartComponent({
if (typeof v !== 'number' || !Number.isFinite(v)) return '—'
const u = unit || ''
if (u.includes('%')) return `${v.toFixed(1)}%`
- if (u.toLowerCase() === 'latency') return formatLatency(v)
+ if (u.toLowerCase() === 'latency') return formatChartLatency(v)
if (u.toLowerCase().includes('ms')) return `${Math.round(v)}ms`
if (u.toLowerCase().includes('exec')) return `${Math.round(v)}`
return `${Math.round(v)}${u}`
@@ -654,31 +607,15 @@ function LineChartComponent({
const len = `${labelStr} ${valueStr}`.length
return Math.max(m, len)
}, 0)
- const tooltipMaxW = Math.min(220, Math.max(80, 7 * longest + 24))
- const anchorX = hoverPos?.x ?? pt.x
- const margin = 10
- const preferRight = anchorX + margin + tooltipMaxW <= width - padding.right
- const left = preferRight
- ? Math.max(
- padding.left,
- Math.min(anchorX + margin, width - padding.right - tooltipMaxW)
- )
- : Math.max(
- padding.left,
- Math.min(anchorX - margin - tooltipMaxW, width - padding.right - tooltipMaxW)
- )
- const anchorY = hoverPos?.y ?? pt.y
- const top = Math.min(Math.max(anchorY - 26, padding.top), height - padding.bottom - 18)
+ const { left, top } = positionChartTooltip({
+ anchorX: hoverPos?.x ?? pt.x,
+ anchorY: hoverPos?.y ?? pt.y,
+ width,
+ height,
+ tooltipMaxWidth: estimateTooltipWidth(longest),
+ })
return (
-
- {currentHoverDate && (
-
- {currentHoverDate}
-
- )}
+
{toDisplay.map((s) => {
const seriesIndex = allSeries.findIndex((x) => x.id === s.id)
const val = allSeries[seriesIndex]?.data?.[hoverIndex]?.value
@@ -686,19 +623,15 @@ function LineChartComponent({
const showLabel =
seriesLabel && seriesLabel !== 'base' && seriesLabel.trim() !== ''
return (
-
-
- {showLabel && (
- {seriesLabel}
- )}
- {fmt(val)}
-
+
)
})}
-
+
)
})()}
diff --git a/apps/sim/components/charts/use-chart-theme.ts b/apps/sim/components/charts/use-chart-theme.ts
new file mode 100644
index 00000000000..30bcd9fd588
--- /dev/null
+++ b/apps/sim/components/charts/use-chart-theme.ts
@@ -0,0 +1,84 @@
+'use client'
+
+import { type RefObject, useEffect, useRef, useState } from 'react'
+import { CHART_MIN_WIDTH } from '@/components/charts/chart-geometry'
+
+/**
+ * Whether the document is in dark mode, tracked by observing the class the theme
+ * toggle writes. Charts need this as a *value* rather than a CSS class because SVG
+ * stroke opacity and blend mode are set per element, not by a selector.
+ */
+export function useIsDarkTheme(): boolean {
+ const [isDark, setIsDark] = useState(true)
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return
+ const element = document.documentElement
+ const update = () => setIsDark(element.classList.contains('dark'))
+ update()
+ const observer = new MutationObserver(update)
+ observer.observe(element, { attributes: true, attributeFilter: ['class'] })
+ return () => observer.disconnect()
+ }, [])
+
+ return isDark
+}
+
+/**
+ * Resolves `var(--token)` colors to concrete `rgb()` strings.
+ *
+ * SVG `stroke` and gradient `stopColor` do not accept a CSS variable that is defined
+ * on an ancestor, so the value has to be computed once and passed literally. Callers
+ * still declare colors as tokens; this is the one place that materializes them.
+ */
+export function useResolvedChartColors(colors: Record): Record {
+ const [resolved, setResolved] = useState>({})
+ const serialized = JSON.stringify(colors)
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return
+
+ const resolveColor = (value: string): string => {
+ if (!value.startsWith('var(')) return value
+ const probe = document.createElement('div')
+ probe.style.color = value
+ document.body.appendChild(probe)
+ const computed = window.getComputedStyle(probe).color
+ probe.remove()
+ return computed
+ }
+
+ const next: Record = {}
+ for (const [key, value] of Object.entries(JSON.parse(serialized) as Record)) {
+ next[key] = resolveColor(value)
+ }
+ setResolved(next)
+ }, [serialized])
+
+ return resolved
+}
+
+/**
+ * Observed container width, floored at {@link CHART_MIN_WIDTH}. `null` until the
+ * first measurement, which callers render as an empty box of the right height so the
+ * chart does not reflow the page when it appears.
+ */
+export function useChartWidth(): [RefObject, number | null] {
+ const containerRef = useRef(null)
+ const [width, setWidth] = useState(null)
+
+ useEffect(() => {
+ const element = containerRef.current
+ if (!element) return
+ const observer = new ResizeObserver((entries) => {
+ const measured = entries[0]?.contentRect?.width
+ if (measured && measured > 0) setWidth(Math.max(CHART_MIN_WIDTH, Math.floor(measured)))
+ })
+ observer.observe(element)
+ const rect = element.getBoundingClientRect()
+ if (rect?.width > 0) setWidth(Math.max(CHART_MIN_WIDTH, Math.floor(rect.width)))
+ return () => observer.disconnect()
+ }, [])
+
+ return [containerRef, width]
+}
diff --git a/apps/sim/components/emails/render-notifications.test.ts b/apps/sim/components/emails/render-notifications.test.ts
index fb5286cfc08..49e6f08a881 100644
--- a/apps/sim/components/emails/render-notifications.test.ts
+++ b/apps/sim/components/emails/render-notifications.test.ts
@@ -73,7 +73,7 @@ describe('renderUsageLimitReachedEmail', () => {
scope: 'organization',
currentUsage: 500,
limit: 500,
- ctaLink: 'https://sim.ai/organization/org_1/settings/billing',
+ ctaLink: 'https://sim.ai/workspace/ws_1/settings/billing',
})
expect(html).toContain('Raise Organization Limit')
diff --git a/apps/sim/components/settings/navigation.test.ts b/apps/sim/components/settings/navigation.test.ts
index 8420aba40bb..df3a5f0d5cd 100644
--- a/apps/sim/components/settings/navigation.test.ts
+++ b/apps/sim/components/settings/navigation.test.ts
@@ -11,17 +11,15 @@ import {
buildUnifiedSettingsNavigation,
canMutateWorkspaceSettingsSection,
getAccountSettingsHref,
- getOrganizationSettingsHref,
getWorkspaceSettingsHref,
isOrganizationSettingsSectionAvailable,
ORGANIZATION_PLANE_UNIFIED_SECTIONS,
- ORGANIZATION_SETTINGS_ITEMS,
- ORGANIZATION_SETTINGS_PATH_ALIASES,
parseSettingsPathSection,
resolveOrganizationSectionAccess,
resolveWorkspaceNavigation,
SELFHOST_SETTINGS_ITEMS,
SETTINGS_SECTION_REGISTRY,
+ UNIFIED_TO_ORGANIZATION_SECTION,
WORKSPACE_SETTINGS_ITEMS,
WORKSPACE_SETTINGS_PATH_ALIASES,
} from '@/components/settings/navigation'
@@ -55,6 +53,7 @@ describe('settings navigation boundaries', () => {
'billing',
'teammates',
'organization',
+ 'usage',
'secrets',
'credential-groups',
'custom-tools',
@@ -83,17 +82,6 @@ describe('settings navigation boundaries', () => {
'mothership',
])
expect(SELFHOST_SETTINGS_ITEMS.map(({ id }) => id)).toEqual(['general', 'billing', 'chat-keys'])
- expect(ORGANIZATION_SETTINGS_ITEMS.map(({ id }) => id)).toEqual([
- 'members',
- 'billing',
- 'access-control',
- 'audit-logs',
- 'sso',
- 'sessions',
- 'data-retention',
- 'data-drains',
- 'whitelabeling',
- ])
expect(WORKSPACE_SETTINGS_ITEMS.map(({ id }) => id)).toEqual([
'teammates',
'secrets',
@@ -199,9 +187,6 @@ describe('settings navigation boundaries', () => {
const accountIds = SETTINGS_SECTION_REGISTRY.flatMap(({ planes }) =>
planes?.account ? [planes.account.id] : []
)
- const organizationIds = SETTINGS_SECTION_REGISTRY.flatMap(({ planes }) =>
- planes?.organization ? [planes.organization.id] : []
- )
const selfHostIds = SETTINGS_SECTION_REGISTRY.flatMap(({ planes }) =>
planes?.selfhost ? [planes.selfhost.id] : []
)
@@ -211,7 +196,6 @@ describe('settings navigation boundaries', () => {
expect(new Set(unifiedIds).size).toBe(unifiedIds.length)
expect(new Set(accountIds).size).toBe(accountIds.length)
- expect(new Set(organizationIds).size).toBe(organizationIds.length)
expect(new Set(selfHostIds).size).toBe(selfHostIds.length)
expect(new Set(workspaceIds).size).toBe(workspaceIds.length)
expect([...unifiedIds].sort()).toEqual(
@@ -220,9 +204,6 @@ describe('settings navigation boundaries', () => {
.sort()
)
expect([...accountIds].sort()).toEqual(ACCOUNT_SETTINGS_ITEMS.map(({ id }) => id).sort())
- expect([...organizationIds].sort()).toEqual(
- ORGANIZATION_SETTINGS_ITEMS.map(({ id }) => id).sort()
- )
expect([...selfHostIds].sort()).toEqual(SELFHOST_SETTINGS_ITEMS.map(({ id }) => id).sort())
expect([...workspaceIds].sort()).toEqual(WORKSPACE_SETTINGS_ITEMS.map(({ id }) => id).sort())
})
@@ -237,27 +218,37 @@ describe('settings navigation boundaries', () => {
'organization',
'sessions',
'sso',
+ 'usage',
'whitelabeling',
])
})
- it('shares labels, icons, and docs links across projections', () => {
- const unifiedSso = buildUnifiedSettingsNavigation().find(({ id }) => id === 'sso')
- const organizationSso = ORGANIZATION_SETTINGS_ITEMS.find(({ id }) => id === 'sso')
-
- expect(organizationSso?.label).toBe(unifiedSso?.label)
- expect(organizationSso?.icon).toBe(unifiedSso?.icon)
- expect(organizationSso?.docsLink).toBe(unifiedSso?.docsLink)
+ it('maps every organization-scoped unified section to its organization counterpart', () => {
+ // The section page reads this map to decide whether to apply the organization
+ // gate at all, so a section missing from it is not "ungated by omission" — it
+ // is a section any workspace member could open.
+ expect(UNIFIED_TO_ORGANIZATION_SECTION).toEqual({
+ organization: 'members',
+ billing: 'billing',
+ 'access-control': 'access-control',
+ 'audit-logs': 'audit-logs',
+ sso: 'sso',
+ sessions: 'sessions',
+ 'data-retention': 'data-retention',
+ 'data-drains': 'data-drains',
+ whitelabeling: 'whitelabeling',
+ usage: 'usage',
+ })
+ expect(Object.keys(UNIFIED_TO_ORGANIZATION_SECTION).sort()).toEqual(
+ [...ORGANIZATION_PLANE_UNIFIED_SECTIONS].sort()
+ )
})
- it('uses scope-specific labels consistently across settings surfaces', () => {
- const organizationMembers = ORGANIZATION_SETTINGS_ITEMS.find(({ id }) => id === 'members')
+ it('labels the members section consistently', () => {
const unifiedOrganization = buildUnifiedSettingsNavigation().find(
({ id }) => id === 'organization'
)
- expect(organizationMembers?.label).toBe('Members')
- expect(organizationMembers?.description).toBe('Manage organization members, roles, and seats.')
expect(unifiedOrganization?.label).toBe('Members')
})
@@ -293,9 +284,6 @@ describe('settings navigation boundaries', () => {
it('builds canonical settings hrefs across all three planes', () => {
expect(getAccountSettingsHref('general')).toBe('/account/settings/general')
- expect(getOrganizationSettingsHref('organization-a', 'members')).toBe(
- '/organization/organization-a/settings/members'
- )
expect(getWorkspaceSettingsHref('workspace-a', 'teammates')).toBe(
'/workspace/workspace-a/settings/teammates'
)
@@ -328,20 +316,6 @@ describe('settings navigation boundaries', () => {
expect(parseAccountPath('/account/settings', 'general')).toBe('general')
})
- it('parses canonical, aliased, and invalid organization settings paths', () => {
- const parseOrganizationPath = (path: string) =>
- parseSettingsPathSection({
- path,
- items: ORGANIZATION_SETTINGS_ITEMS,
- defaultSection: null,
- aliases: ORGANIZATION_SETTINGS_PATH_ALIASES,
- })
-
- expect(parseOrganizationPath('sso')).toBe('sso')
- expect(parseOrganizationPath('/organization/org-a/settings/organization')).toBe('members')
- expect(parseOrganizationPath('/organization/org-a/settings/not-a-section')).toBeNull()
- })
-
it('parses canonical, aliased, and invalid workspace settings paths', () => {
const parseWorkspacePath = (path: string) =>
parseSettingsPathSection({
@@ -359,7 +333,6 @@ describe('settings navigation boundaries', () => {
it('keeps API keys split between account and workspace settings', () => {
expect(ACCOUNT_SETTINGS_ITEMS.some(({ id }) => id === 'api-keys')).toBe(true)
expect(WORKSPACE_SETTINGS_ITEMS.some(({ id }) => id === 'api-keys')).toBe(true)
- expect(ORGANIZATION_SETTINGS_ITEMS.some(({ id }) => String(id) === 'api-keys')).toBe(false)
})
it('requires target-organization membership and admin authority', () => {
diff --git a/apps/sim/components/settings/navigation.ts b/apps/sim/components/settings/navigation.ts
index 64704f531d5..dcb92ef1beb 100644
--- a/apps/sim/components/settings/navigation.ts
+++ b/apps/sim/components/settings/navigation.ts
@@ -1,5 +1,6 @@
import type { ComponentType } from 'react'
import {
+ ChartColumn,
ClipboardList,
Clock,
Credit,
@@ -40,10 +41,11 @@ import {
isSandboxesEnabled,
isSessionPoliciesEnabled,
isSsoEnabled,
+ isUsageMonitoringEnabled,
isWhitelabelingEnabled,
} from '@/lib/core/config/env-flags'
-export type SettingsPlane = 'account' | 'organization' | 'selfhost' | 'workspace'
+export type SettingsPlane = 'account' | 'selfhost' | 'workspace'
export type AccountSettingsSection = 'general' | 'billing' | 'api-keys' | 'admin' | 'mothership'
@@ -56,6 +58,7 @@ export type SelfHostSettingsSection = 'general' | 'billing' | 'chat-keys'
export type OrganizationSettingsSection =
| 'members'
| 'billing'
+ | 'usage'
| 'access-control'
| 'audit-logs'
| 'sso'
@@ -86,8 +89,6 @@ export type SettingsSection =
| SelfHostSettingsSection
| WorkspaceSettingsSection
-export type OrganizationSettingsRouteSection = OrganizationSettingsSection | 'unavailable'
-
export interface SettingsNavigationItem {
id: Section
label: string
@@ -112,6 +113,7 @@ export type UnifiedSettingsSection =
| 'billing'
| 'teammates'
| 'organization'
+ | 'usage'
| 'sso'
| 'whitelabeling'
| 'forks'
@@ -164,6 +166,16 @@ export interface UnifiedSettingsNavigationItem {
hideForEnterprise?: boolean
externalUrl?: string
docsLink?: string
+ /**
+ * The organization-scoped counterpart of this section. Declaring it marks the
+ * section as acting on the host organization rather than the workspace, which
+ * routes it through the organization gate (host organization present, org-admin
+ * viewer, plan entitlement) in both the sidebar and the section page.
+ *
+ * This is the single source for {@link ORGANIZATION_PLANE_UNIFIED_SECTIONS} and
+ * {@link UNIFIED_TO_ORGANIZATION_SECTION}, so the two cannot drift apart.
+ */
+ organizationSection?: OrganizationSettingsSection
}
interface UnifiedSettingsProjection
@@ -173,7 +185,6 @@ interface UnifiedSettingsProjection
interface SettingsPlaneSectionMap {
account: AccountSettingsSection
- organization: OrganizationSettingsSection
selfhost: SelfHostSettingsSection
workspace: WorkspaceSettingsSection
}
@@ -220,6 +231,7 @@ const SETTINGS_SELF_HOSTED_OVERRIDES = {
sandboxes: isSandboxesEnabled,
sessionPolicies: isSessionPoliciesEnabled,
sso: isSsoEnabled,
+ usageMonitoring: isUsageMonitoringEnabled,
whitelabeling: isWhitelabelingEnabled,
} as const
@@ -249,17 +261,6 @@ export function getSelfHostSettingsHref(
return withSettingsSearchParams(`/selfhost/settings/${section}`, searchParams)
}
-export function getOrganizationSettingsHref(
- organizationId: string,
- section: OrganizationSettingsRouteSection,
- searchParams?: SettingsHrefSearchParams
-): string {
- return withSettingsSearchParams(
- `/organization/${organizationId}/settings/${section}`,
- searchParams
- )
-}
-
export function getWorkspaceSettingsHref(
workspaceId: string,
section: WorkspaceSettingsSection,
@@ -272,12 +273,6 @@ export const ACCOUNT_SETTINGS_PATH_ALIASES = {
apikeys: 'api-keys',
} as const satisfies Readonly>
-export const ORGANIZATION_SETTINGS_PATH_ALIASES = {
- organization: 'members',
- // Verified domains moved into the SSO page; keep old links working.
- domains: 'sso',
-} as const satisfies Readonly>
-
export const WORKSPACE_SETTINGS_PATH_ALIASES = {
apikeys: 'api-keys',
} as const satisfies Readonly>
@@ -341,7 +336,6 @@ export const SETTINGS_PLANE_CHROME: Record<
{ label: string; showWordmark: boolean }
> = {
account: { label: 'Account', showWordmark: false },
- organization: { label: 'Organization', showWordmark: false },
selfhost: { label: 'Self-host', showWordmark: true },
}
@@ -350,12 +344,6 @@ export const SELFHOST_SETTINGS_GROUPS = [
{ key: 'developer', title: 'Developer' },
] as const
-export const ORGANIZATION_SETTINGS_GROUPS = [
- { key: 'organization', title: 'Organization' },
- { key: 'security', title: 'Security' },
- { key: 'enterprise', title: 'Enterprise' },
-] as const
-
export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[] = [
{
label: 'General',
@@ -412,13 +400,11 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
id: 'access-control',
description: 'Manage permission groups across your organization.',
group: 'organization',
- order: 3,
+ order: 4,
requiresHosted: true,
requiresEnterprise: true,
selfHostedOverride: SETTINGS_SELF_HOSTED_OVERRIDES.accessControl,
- },
- planes: {
- organization: { id: 'access-control', group: 'security', order: 2 },
+ organizationSection: 'access-control',
},
},
{
@@ -429,13 +415,11 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
id: 'audit-logs',
description: 'Review activity and changes across your organization.',
group: 'organization',
- order: 4,
+ order: 5,
requiresHosted: true,
requiresEnterprise: true,
selfHostedOverride: SETTINGS_SELF_HOSTED_OVERRIDES.auditLogs,
- },
- planes: {
- organization: { id: 'audit-logs', group: 'security', order: 3 },
+ organizationSection: 'audit-logs',
},
},
{
@@ -446,7 +430,7 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
id: 'forks',
description: 'Fork this workspace and sync changes with its parent.',
group: 'organization',
- order: 2,
+ order: 3,
},
planes: {
workspace: { id: 'forks', group: 'enterprise', order: 10 },
@@ -461,6 +445,7 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
group: 'account',
order: 1,
hideWhenBillingDisabled: true,
+ organizationSection: 'billing',
},
planes: {
account: {
@@ -475,12 +460,6 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
group: 'account',
order: 1,
},
- organization: {
- id: 'billing',
- description: 'Manage the organization plan, usage, and invoices.',
- group: 'organization',
- order: 1,
- },
},
},
{
@@ -507,14 +486,38 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
hideWhenBillingDisabled: true,
requiresHosted: true,
requiresTeam: true,
+ /**
+ * A plain member sees the roster read-only — `resolveOrganizationSectionAccess`
+ * grants them `'view'` on this one section, and `TeamManagement` renders
+ * without management controls. Every other organization section stays
+ * admin-only.
+ */
+ allowNonOrgAdmin: true,
+ organizationSection: 'members',
},
- planes: {
- organization: {
- id: 'members',
- description: 'Manage organization members, roles, and seats.',
- group: 'organization',
- order: 0,
- },
+ },
+ {
+ label: 'Usage tracking',
+ icon: ChartColumn,
+ unified: {
+ id: 'usage',
+ description: 'Monitor credit usage across your organization.',
+ group: 'organization',
+ order: 1,
+ /**
+ * Deliberately no `hideWhenBillingDisabled`, unlike Members above.
+ *
+ * The sidebar applies that filter *before* it consults `selfHostedOverride`,
+ * so pairing the two hid this section from exactly the deployment the
+ * override exists to serve: self-hosted, billing off, `USAGE_MONITORING_ENABLED`
+ * on. Members can carry the flag because it has no override to reach. Here the
+ * two gates below already answer both cases — hosted needs the plan, and
+ * self-hosted needs the flag.
+ */
+ requiresHosted: true,
+ requiresEnterprise: true,
+ selfHostedOverride: SETTINGS_SELF_HOSTED_OVERRIDES.usageMonitoring,
+ organizationSection: 'usage',
},
},
{
@@ -704,13 +707,11 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
id: 'sso',
description: 'Configure single sign-on for your organization.',
group: 'organization',
- order: 6,
+ order: 7,
requiresHosted: true,
requiresEnterprise: true,
selfHostedOverride: SETTINGS_SELF_HOSTED_OVERRIDES.sso,
- },
- planes: {
- organization: { id: 'sso', group: 'security', order: 4 },
+ organizationSection: 'sso',
},
},
{
@@ -721,13 +722,11 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
id: 'sessions',
description: 'Limit session lifetimes and sign out members org-wide.',
group: 'organization',
- order: 7,
+ order: 8,
requiresHosted: true,
requiresEnterprise: true,
selfHostedOverride: SETTINGS_SELF_HOSTED_OVERRIDES.sessionPolicies,
- },
- planes: {
- organization: { id: 'sessions', group: 'security', order: 5 },
+ organizationSection: 'sessions',
},
},
{
@@ -739,13 +738,11 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
description:
'Control data retention windows and PII redaction. Workspaces without an override inherit the organization defaults.',
group: 'organization',
- order: 8,
+ order: 9,
requiresHosted: true,
requiresEnterprise: true,
selfHostedOverride: SETTINGS_SELF_HOSTED_OVERRIDES.dataRetention,
- },
- planes: {
- organization: { id: 'data-retention', group: 'enterprise', order: 6 },
+ organizationSection: 'data-retention',
},
},
{
@@ -756,13 +753,11 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
id: 'data-drains',
description: 'Stream your logs and events to external destinations.',
group: 'organization',
- order: 9,
+ order: 10,
requiresHosted: true,
requiresEnterprise: true,
selfHostedOverride: SETTINGS_SELF_HOSTED_OVERRIDES.dataDrains,
- },
- planes: {
- organization: { id: 'data-drains', group: 'enterprise', order: 7 },
+ organizationSection: 'data-drains',
},
},
{
@@ -773,13 +768,11 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
id: 'whitelabeling',
description: 'Customize your workspace branding and appearance.',
group: 'organization',
- order: 5,
+ order: 6,
requiresHosted: true,
requiresEnterprise: true,
selfHostedOverride: SETTINGS_SELF_HOSTED_OVERRIDES.whitelabeling,
- },
- planes: {
- organization: { id: 'whitelabeling', group: 'enterprise', order: 8 },
+ organizationSection: 'whitelabeling',
},
},
{
@@ -790,7 +783,7 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[]
id: 'custom-blocks',
description: 'Publish workflows as reusable blocks for your organization.',
group: 'organization',
- order: 1,
+ order: 2,
requiresHosted: true,
requiresEnterprise: true,
allowNonOrgAdmin: true,
@@ -878,9 +871,6 @@ function buildPlaneSettingsItems(
export const ACCOUNT_SETTINGS_ITEMS: SettingsNavigationItem[] =
buildPlaneSettingsItems('account')
-export const ORGANIZATION_SETTINGS_ITEMS: SettingsNavigationItem[] =
- buildPlaneSettingsItems('organization')
-
export const SELFHOST_SETTINGS_ITEMS: SettingsNavigationItem[] =
buildPlaneSettingsItems('selfhost')
@@ -895,7 +885,26 @@ export const WORKSPACE_SETTINGS_ITEMS: SettingsNavigationItem = new Set(
SETTINGS_SECTION_REGISTRY.flatMap((entry) =>
- entry.planes?.organization && entry.unified ? [entry.unified.id] : []
+ entry.unified?.organizationSection ? [entry.unified.id] : []
+ )
+)
+
+/**
+ * Unified section id to the organization-scoped section it acts on, for the gates
+ * that take an {@link OrganizationSettingsSection} (`canOpenOrganizationSettingsSection`,
+ * `isOrganizationSettingsSectionAvailable`).
+ *
+ * Derived from the registry rather than hand-listed: a section added to one and
+ * forgotten in the other used to mean the page applied *no* organization gate at
+ * all, since an unmapped section reads as "not organization-scoped".
+ */
+export const UNIFIED_TO_ORGANIZATION_SECTION: Readonly<
+ Partial>
+> = Object.fromEntries(
+ SETTINGS_SECTION_REGISTRY.flatMap((entry) =>
+ entry.unified?.organizationSection
+ ? [[entry.unified.id, entry.unified.organizationSection] as const]
+ : []
)
)
@@ -938,6 +947,7 @@ export function getOrganizationSettingsFeatures(
sessions: SETTINGS_SELF_HOSTED_OVERRIDES.sessionPolicies,
'data-retention': SETTINGS_SELF_HOSTED_OVERRIDES.dataRetention,
'data-drains': SETTINGS_SELF_HOSTED_OVERRIDES.dataDrains,
+ usage: SETTINGS_SELF_HOSTED_OVERRIDES.usageMonitoring,
whitelabeling: SETTINGS_SELF_HOSTED_OVERRIDES.whitelabeling,
},
}
@@ -1097,11 +1107,9 @@ export function getSettingsSectionMeta(
const catalog =
plane === 'account'
? ACCOUNT_SETTINGS_ITEMS
- : plane === 'organization'
- ? ORGANIZATION_SETTINGS_ITEMS
- : plane === 'selfhost'
- ? SELFHOST_SETTINGS_ITEMS
- : WORKSPACE_SETTINGS_ITEMS
+ : plane === 'selfhost'
+ ? SELFHOST_SETTINGS_ITEMS
+ : WORKSPACE_SETTINGS_ITEMS
const item = catalog.find((candidate) => candidate.id === section)
return item ? { label: item.label, description: item.description, docsLink: item.docsLink } : null
}
diff --git a/apps/sim/components/settings/organization-settings-renderer.tsx b/apps/sim/components/settings/organization-settings-renderer.tsx
deleted file mode 100644
index 66ae7a1efc3..00000000000
--- a/apps/sim/components/settings/organization-settings-renderer.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-'use client'
-
-import { useEffect } from 'react'
-import dynamic from 'next/dynamic'
-import { usePostHog } from 'posthog-js/react'
-import type { OrganizationSettingsSection } from '@/components/settings/navigation'
-import { captureEvent } from '@/lib/posthog/client'
-
-const TeamManagement = dynamic(() =>
- import('@/app/workspace/[workspaceId]/settings/components/team-management/team-management').then(
- (module) => module.TeamManagement
- )
-)
-const Billing = dynamic(() =>
- import('@/app/workspace/[workspaceId]/settings/components/billing/billing').then(
- (module) => module.Billing
- )
-)
-const AccessControl = dynamic(() =>
- import('@/ee/access-control/components/access-control').then((module) => module.AccessControl)
-)
-const AuditLogs = dynamic(() =>
- import('@/ee/audit-logs/components/audit-logs').then((module) => module.AuditLogs)
-)
-const SSO = dynamic(() => import('@/ee/sso/components/sso-settings').then((module) => module.SSO))
-const SessionPolicySettings = dynamic(() =>
- import('@/ee/session-policy/components/session-policy-settings').then(
- (module) => module.SessionPolicySettings
- )
-)
-const DataRetentionSettings = dynamic(() =>
- import('@/ee/data-retention/components/data-retention-settings').then(
- (module) => module.DataRetentionSettings
- )
-)
-const DataDrainsSettings = dynamic(() =>
- import('@/ee/data-drains/components/data-drains-settings').then(
- (module) => module.DataDrainsSettings
- )
-)
-const WhitelabelingSettings = dynamic(
- () =>
- import('@/ee/whitelabeling/components/whitelabeling-settings').then(
- (module) => module.WhitelabelingSettings
- ),
- { ssr: false }
-)
-
-interface OrganizationSettingsRendererProps {
- organizationId: string
- section: OrganizationSettingsSection
-}
-
-export function OrganizationSettingsRenderer({
- organizationId,
- section,
-}: OrganizationSettingsRendererProps) {
- const posthog = usePostHog()
-
- useEffect(() => {
- captureEvent(posthog, 'settings_tab_viewed', { plane: 'organization', section })
- }, [posthog, section])
-
- if (section === 'members') return
- if (section === 'billing') return
- if (section === 'access-control') {
- return
- }
- if (section === 'audit-logs') return
- if (section === 'sso') return
- if (section === 'sessions') {
- return
- }
- if (section === 'data-retention') {
- return
- }
- if (section === 'data-drains') return
- return
-}
diff --git a/apps/sim/components/settings/settings-header-shell.test.tsx b/apps/sim/components/settings/settings-header-shell.test.tsx
index e2d588159ff..ecf53c2706a 100644
--- a/apps/sim/components/settings/settings-header-shell.test.tsx
+++ b/apps/sim/components/settings/settings-header-shell.test.tsx
@@ -181,8 +181,8 @@ describe('SettingsHeaderShell static meta', () => {
})
it('lets a body claim the header with nothing in it, suppressing the meta entirely', () => {
- // How SettingsUnavailable opts out: it renders its own centred heading, so the routed
- // section's catalog title must not caption it.
+ // How a surface that draws its own centred heading opts out: the routed section's
+ // catalog title must not also caption it.
function OwnHeadingBody() {
useSettingsHeader(EMPTY_HEADER)
return
diff --git a/apps/sim/components/settings/settings-header.tsx b/apps/sim/components/settings/settings-header.tsx
index 7c1b3ae4c69..27788ef7293 100644
--- a/apps/sim/components/settings/settings-header.tsx
+++ b/apps/sim/components/settings/settings-header.tsx
@@ -274,7 +274,7 @@ export function SettingsHeaderShell({ meta, children }: SettingsHeaderShellProps
* field-by-field: a body that registers an explicit `title` and no `description` is
* deliberately suppressing the meta description, so the two must never be merged — and a
* body that registers an empty config is deliberately asking for no heading at all, which
- * is how a surface that renders its own (`SettingsUnavailable`) opts out.
+ * is how a surface that renders its own heading opts out.
*/
const config: SettingsHeaderConfig = registered ?? meta ?? EMPTY_CONFIG
const { title, description, docsLink, back, actions, search, scrollContainerRef } = config
diff --git a/apps/sim/components/settings/settings-unavailable.tsx b/apps/sim/components/settings/settings-unavailable.tsx
deleted file mode 100644
index 806ab078c3f..00000000000
--- a/apps/sim/components/settings/settings-unavailable.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { ChipLink, cn } from '@sim/emcn'
-
-interface SettingsUnavailableProps {
- title?: string
- description?: string
- embedded?: boolean
-}
-
-export function SettingsUnavailable({
- title = 'Settings unavailable',
- description = 'You do not have access to manage this organization. Contact an organization owner or admin for help.',
- embedded = false,
-}: SettingsUnavailableProps) {
- return (
-
-
-
{title}
-
{description}
-
Back to workspaces
-
-
- )
-}
diff --git a/apps/sim/components/settings/standalone-settings-shell.test.ts b/apps/sim/components/settings/standalone-settings-shell.test.ts
index 720bcdfff56..49d462e8f76 100644
--- a/apps/sim/components/settings/standalone-settings-shell.test.ts
+++ b/apps/sim/components/settings/standalone-settings-shell.test.ts
@@ -5,8 +5,6 @@ import { describe, expect, it } from 'vitest'
import {
ACCOUNT_SETTINGS_ITEMS,
ACCOUNT_SETTINGS_PATH_ALIASES,
- ORGANIZATION_SETTINGS_ITEMS,
- ORGANIZATION_SETTINGS_PATH_ALIASES,
parseSettingsPathSection,
SELFHOST_SETTINGS_ITEMS,
} from '@/components/settings/navigation'
@@ -23,17 +21,6 @@ describe('standalone settings section resolution', () => {
).toBe('billing')
})
- it('resolves the organization section from its pathname', () => {
- expect(
- parseSettingsPathSection({
- path: '/organization/org-1/settings/audit-logs',
- items: ORGANIZATION_SETTINGS_ITEMS,
- defaultSection: 'members',
- aliases: ORGANIZATION_SETTINGS_PATH_ALIASES,
- })
- ).toBe('audit-logs')
- })
-
it('keeps Subscription active for the self-host billing route', () => {
expect(
parseSettingsPathSection({
diff --git a/apps/sim/components/settings/standalone-settings-shell.tsx b/apps/sim/components/settings/standalone-settings-shell.tsx
index 2991fe68cfe..ac31197e7b6 100644
--- a/apps/sim/components/settings/standalone-settings-shell.tsx
+++ b/apps/sim/components/settings/standalone-settings-shell.tsx
@@ -7,15 +7,8 @@ import {
ACCOUNT_SETTINGS_ITEMS,
ACCOUNT_SETTINGS_PATH_ALIASES,
getAccountSettingsHref,
- getOrganizationSettingsFeatures,
- getOrganizationSettingsHref,
getSelfHostSettingsHref,
- isOrganizationSettingsSectionAvailable,
- ORGANIZATION_SETTINGS_GROUPS,
- ORGANIZATION_SETTINGS_ITEMS,
- ORGANIZATION_SETTINGS_PATH_ALIASES,
parseSettingsPathSection,
- resolveOrganizationSectionAccess,
SELFHOST_SETTINGS_GROUPS,
SELFHOST_SETTINGS_ITEMS,
SETTINGS_PLANE_CHROME,
@@ -40,40 +33,19 @@ interface SelfHostSettingsShellProps extends StandaloneSettingsShellBaseProps {
plane: 'selfhost'
}
-interface OrganizationSettingsShellProps extends StandaloneSettingsShellBaseProps {
- plane: 'organization'
- organizationId: string
- hasEnterprisePlan: boolean
- isOrganizationAdmin: boolean
-}
-
-type StandaloneSettingsShellProps =
- | AccountSettingsShellProps
- | OrganizationSettingsShellProps
- | SelfHostSettingsShellProps
+type StandaloneSettingsShellProps = AccountSettingsShellProps | SelfHostSettingsShellProps
export function StandaloneSettingsShell(props: StandaloneSettingsShellProps) {
const { children, plane } = props
useSettingsBeforeUnload()
const pathname = usePathname()
- const hasEnterprisePlan = plane === 'organization' ? props.hasEnterprisePlan : false
- const isOrganizationAdmin = plane === 'organization' ? props.isOrganizationAdmin : false
const isSuperUser = plane === 'account' ? (props.isSuperUser ?? false) : false
- const organizationFeatures = getOrganizationSettingsFeatures(hasEnterprisePlan)
const accountItems = ACCOUNT_SETTINGS_ITEMS.filter((item) => {
if (item.id === 'billing' && !isBillingEnabled) return false
if ((item.id === 'admin' || item.id === 'mothership') && !isSuperUser) return false
return true
})
- const organizationItems = ORGANIZATION_SETTINGS_ITEMS.filter(
- (item) =>
- resolveOrganizationSectionAccess({
- section: item.id,
- isTargetOrganizationMember: true,
- isTargetOrganizationAdmin: isOrganizationAdmin,
- }) !== 'unavailable' && isOrganizationSettingsSectionAvailable(item.id, organizationFeatures)
- )
const selfHostItems = SELFHOST_SETTINGS_ITEMS.filter((item) => {
if (item.id === 'billing' && !isBillingEnabled) return false
// Chat keys are issued by the managed service, so there are none to list on
@@ -93,28 +65,9 @@ export function StandaloneSettingsShell(props: StandaloneSettingsShellProps) {
defaultSection: 'general',
aliases: ACCOUNT_SETTINGS_PATH_ALIASES,
})
- const organizationSection = parseSettingsPathSection({
- path: pathname,
- items: ORGANIZATION_SETTINGS_ITEMS,
- defaultSection: 'members',
- aliases: ORGANIZATION_SETTINGS_PATH_ALIASES,
- })
- const activeSection =
- plane === 'account'
- ? accountSection
- : plane === 'selfhost'
- ? selfHostSection
- : organizationSection
+ const activeSection = plane === 'account' ? accountSection : selfHostSection
const sidebar =
- plane === 'selfhost' ? (
-
- ) : plane === 'account' ? (
+ plane === 'account' ? (
) : (
getOrganizationSettingsHref(props.organizationId, section)}
- items={organizationItems}
+ groups={SELFHOST_SETTINGS_GROUPS}
+ hrefForSection={getSelfHostSettingsHref}
+ items={selfHostItems}
/>
)
diff --git a/apps/sim/ee/organization-usage/components/usage-consumers.tsx b/apps/sim/ee/organization-usage/components/usage-consumers.tsx
new file mode 100644
index 00000000000..e20de0359ac
--- /dev/null
+++ b/apps/sim/ee/organization-usage/components/usage-consumers.tsx
@@ -0,0 +1,195 @@
+'use client'
+
+import type { ComponentType } from 'react'
+import { cn } from '@sim/emcn'
+import { ChevronRight } from '@sim/emcn/icons'
+import { formatChartCompactNumber } from '@/components/charts'
+import {
+ AnthropicIcon,
+ AzureIcon,
+ CerebrasIcon,
+ DeepseekIcon,
+ GoogleIcon,
+ GroqIcon,
+ MistralIcon,
+ OllamaIcon,
+ OpenAIIcon,
+ OpenRouterIcon,
+ xAIIcon,
+} from '@/components/icons'
+import type {
+ OrganizationUsageBreakdown,
+ OrganizationUsageBreakdownRow,
+ UsageBreakdownDimension,
+} from '@/lib/api/contracts/organization-usage'
+import {
+ type RowAction,
+ RowActionsMenu,
+} from '@/app/workspace/[workspaceId]/settings/components/row-actions-menu'
+import { SettingsEmptyState } from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state'
+import { USAGE_TAB_EMPTY_COPY } from '@/ee/organization-usage/constants'
+
+/**
+ * Provider brand marks, keyed by the `providerId` the server resolves.
+ *
+ * Kept here rather than read from `providers/models.ts`: that module carries the
+ * whole model registry and would land in this settings chunk for two dozen glyphs.
+ */
+const PROVIDER_ICONS: Readonly>> = {
+ openai: OpenAIIcon,
+ anthropic: AnthropicIcon,
+ google: GoogleIcon,
+ 'azure-openai': AzureIcon,
+ deepseek: DeepseekIcon,
+ xai: xAIIcon,
+ groq: GroqIcon,
+ cerebras: CerebrasIcon,
+ ollama: OllamaIcon,
+ openrouter: OpenRouterIcon,
+ mistral: MistralIcon,
+}
+
+interface UsageConsumerRowProps {
+ row: OrganizationUsageBreakdownRow
+ /** BYOK rows carry no cost, so tokens are the only usage they can show. */
+ showTokensOnly: boolean
+ onSelect?: (row: OrganizationUsageBreakdownRow) => void
+ actions?: RowAction[]
+}
+
+/**
+ * Width of each trailing affordance, so a list that carries one can reserve the
+ * same slot on its `Other` row and keep every figure in one column.
+ */
+const TRAILING_SLOT_CLASSES = {
+ /** `ChevronRight` at the platform icon size. */
+ chevron: 'size-[14px]',
+ /** `RowActionsMenu`'s trigger: a 14px glyph in a `chipVariants()` pill. */
+ menu: 'size-[30px]',
+} as const
+
+/**
+ * A tabular row, not `SettingsResourceRow` — tabular columns are the sanctioned
+ * exception in `sim-settings-pages.md`, alongside billing invoices and credit usage.
+ */
+function UsageConsumerRow({ row, showTokensOnly, onSelect, actions }: UsageConsumerRowProps) {
+ const ProviderIcon = row.providerId ? PROVIDER_ICONS[row.providerId] : undefined
+ const Row = onSelect ? 'button' : 'div'
+
+ return (
+ onSelect(row),
+ 'aria-label': `Open ${row.label}`,
+ }
+ : {})}
+ className={cn(
+ 'flex w-full items-center gap-2.5 rounded-lg p-2 text-left',
+ onSelect && 'transition-colors hover:bg-[var(--surface-active)]'
+ )}
+ >
+ {ProviderIcon && (
+
+ )}
+ {row.label}
+
+
+ {showTokensOnly ? formatChartCompactNumber(row.tokens ?? 0) : row.credits.toLocaleString()}
+
+ {/* A chevron or a menu, never both — `sim-settings-pages.md`. */}
+ {onSelect ? (
+
+ ) : actions?.length ? (
+
+ ) : null}
+
+ )
+}
+
+interface UsageConsumersProps {
+ dimension: UsageBreakdownDimension
+ breakdown?: OrganizationUsageBreakdown
+ isLoading: boolean
+ isError: boolean
+ /** Set on Workspaces, where a row drills into that workspace's workflows. */
+ onSelectRow?: (row: OrganizationUsageBreakdownRow) => void
+ /** Set on Members, where a row can open the shared manage-credits modal. */
+ rowActions?: (row: OrganizationUsageBreakdownRow) => RowAction[]
+}
+
+export function UsageConsumers({
+ dimension,
+ breakdown,
+ isLoading,
+ isError,
+ onSelectRow,
+ rowActions,
+}: UsageConsumersProps) {
+ if (isError) {
+ return (
+
+ Couldn't load this view.
+
+ )
+ }
+ if (isLoading || !breakdown) {
+ return Loading…
+ }
+ if (breakdown.rows.length === 0) {
+ return (
+ {USAGE_TAB_EMPTY_COPY[dimension]}
+ )
+ }
+
+ const showTokensOnly = dimension === 'byok'
+ const trailingSlot = onSelectRow
+ ? TRAILING_SLOT_CLASSES.chevron
+ : rowActions
+ ? TRAILING_SLOT_CLASSES.menu
+ : null
+
+ return (
+
+ {breakdown.rows.map((row) => (
+
+ ))}
+ {/*
+ The truncated tail, named rather than dropped: a ranked list that does not add
+ up to the headline figure is how "the numbers are wrong" reports start.
+ */}
+ {breakdown.other.rowCount > 0 && (
+
+
+ {`Other (${breakdown.other.rowCount} more)`}
+
+
+ {showTokensOnly
+ ? formatChartCompactNumber(breakdown.other.tokens)
+ : breakdown.other.credits.toLocaleString()}
+
+ {trailingSlot && (
+
+ )}
+
+ )}
+
+ )
+}
diff --git a/apps/sim/ee/organization-usage/components/usage-events-view.tsx b/apps/sim/ee/organization-usage/components/usage-events-view.tsx
new file mode 100644
index 00000000000..7a499cc77b3
--- /dev/null
+++ b/apps/sim/ee/organization-usage/components/usage-events-view.tsx
@@ -0,0 +1,117 @@
+'use client'
+
+import { chipVariants, cn } from '@sim/emcn'
+import { ArrowLeft } from '@sim/emcn/icons'
+import { formatDateTime } from '@sim/utils/formatting'
+import { useRouter } from 'next/navigation'
+import type { OrganizationUsageEvent } from '@/lib/api/contracts/organization-usage'
+import { formatApportionedCreditCost } from '@/lib/billing/credits/conversion'
+import {
+ BILLING_USAGE_LOG_SOURCE_LABELS,
+ type InternalUsageLogSource,
+ toBillingUsageLogSource,
+} from '@/lib/billing/usage-sources'
+import { SettingsEmptyState } from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state'
+import { SettingsPanel } from '@/app/workspace/[workspaceId]/settings/components/settings-panel'
+import { useUsageWindow } from '@/ee/organization-usage/hooks/use-usage-window'
+import { useOrganizationUsageEvents } from '@/hooks/queries/organization-usage'
+
+/** A workflow row names its workflow; everything else reads as its product surface. */
+function eventLabel(event: OrganizationUsageEvent): string {
+ if (event.source === 'workflow' && event.workflowName) return `Workflow: ${event.workflowName}`
+ return BILLING_USAGE_LOG_SOURCE_LABELS[
+ toBillingUsageLogSource(event.source as InternalUsageLogSource)
+ ]
+}
+
+function UsageEventRow({ event }: { event: OrganizationUsageEvent }) {
+ return (
+
+
+ {formatDateTime(new Date(event.createdAt))}
+
+
+ {eventLabel(event)}
+
+
+ {event.description}
+
+
+ {formatApportionedCreditCost(event.credits, event.hasCost)}
+
+
+ )
+}
+
+interface UsageEventsViewProps {
+ organizationId: string
+ backHref: string
+}
+
+/**
+ * The organization's raw ledger.
+ *
+ * A drill-down rather than a section on the usage panel, mirroring how Billing puts
+ * credit usage behind a link: the panel answers whether spend is healthy, this
+ * answers what a specific charge was.
+ */
+export function UsageEventsView({ organizationId, backHref }: UsageEventsViewProps) {
+ const router = useRouter()
+ const { window } = useUsageWindow()
+ const {
+ data,
+ isLoading,
+ isError,
+ isPlaceholderData,
+ hasNextPage,
+ isFetchingNextPage,
+ fetchNextPage,
+ } = useOrganizationUsageEvents(organizationId, window)
+
+ const events = data?.pages.flatMap((page) => page.events) ?? []
+
+ return (
+ router.push(backHref) }}
+ title='Usage events'
+ description='Every credit-consuming event behind your usage.'
+ >
+
+ {isLoading ? (
+ Loading usage…
+ ) : isError ? (
+
+ Couldn't load usage events.
+
+ ) : events.length === 0 ? (
+ No usage in this period.
+ ) : (
+ <>
+ {events.map((event) => (
+
+ ))}
+ {hasNextPage && (
+ fetchNextPage()}
+ disabled={isFetchingNextPage}
+ aria-label='Load more usage events'
+ className={cn(
+ chipVariants({ fullWidth: true }),
+ 'text-[var(--text-muted)] text-small'
+ )}
+ >
+ {isFetchingNextPage ? 'Loading…' : 'Load more'}
+
+ )}
+ >
+ )}
+
+
+ )
+}
diff --git a/apps/sim/ee/organization-usage/components/usage-monitoring.tsx b/apps/sim/ee/organization-usage/components/usage-monitoring.tsx
new file mode 100644
index 00000000000..0708d28a4cc
--- /dev/null
+++ b/apps/sim/ee/organization-usage/components/usage-monitoring.tsx
@@ -0,0 +1,404 @@
+'use client'
+
+import type { ReactNode } from 'react'
+import { useState } from 'react'
+import {
+ Calendar,
+ ChipCombobox,
+ ChipModalTabs,
+ Popover,
+ PopoverAnchor,
+ PopoverContent,
+ toast,
+} from '@sim/emcn'
+import { ArrowLeft, Download } from '@sim/emcn/icons'
+import { useRouter } from 'next/navigation'
+import {
+ MAX_CUSTOM_RANGE_DAYS,
+ type UsageBreakdownDimension,
+} from '@/lib/api/contracts/organization-usage'
+import { dollarsToCredits } from '@/lib/billing/credits/conversion'
+import { isHosted } from '@/lib/core/config/env-flags'
+import {
+ ManageCreditsModal,
+ type ManageCreditsTarget,
+} from '@/app/workspace/[workspaceId]/settings/components/manage-credits-modal'
+import { SettingsPanel } from '@/app/workspace/[workspaceId]/settings/components/settings-panel'
+import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section'
+import { UsageConsumers } from '@/ee/organization-usage/components/usage-consumers'
+import { UsageSummary } from '@/ee/organization-usage/components/usage-summary'
+import {
+ PERIOD_OPTIONS,
+ USAGE_OVERVIEW_TAB,
+ USAGE_SECTION_LABELS,
+ USAGE_TAB_LABELS,
+ USAGE_TAB_ORDER,
+ type UsageTab,
+} from '@/ee/organization-usage/constants'
+import { useUsageWindow } from '@/ee/organization-usage/hooks/use-usage-window'
+import { useOrganizationBilling } from '@/hooks/queries/organization'
+import {
+ useOrganizationUsageBreakdown,
+ useOrganizationUsageSummary,
+} from '@/hooks/queries/organization-usage'
+
+const TABS = USAGE_TAB_ORDER.map((tab) => ({ value: tab, label: USAGE_TAB_LABELS[tab] }))
+
+const DAY_MS = 24 * 60 * 60 * 1000
+
+/**
+ * One labelled band per view. The unit lives here rather than on every row — ten rows
+ * each ending in the word "credits" is noise, and a column header is where a reader
+ * already looks for it.
+ */
+function UsageSection({
+ dimension,
+ unit,
+ children,
+}: {
+ dimension: UsageBreakdownDimension
+ unit: 'credits' | 'tokens'
+ children: ReactNode
+}) {
+ return (
+ {unit}}
+ >
+ {children}
+
+ )
+}
+
+interface UsageMonitoringProps {
+ organizationId: string
+ /** Set by the settings section switch; the sub-route lives under this workspace. */
+ workspaceId: string
+}
+
+/**
+ * Organization usage monitoring.
+ *
+ * The panel reads as one question per tab: how much and what kind of work
+ * (Overview), then who (Members), where (Workspaces), and on what (Models, BYOK).
+ * Only the visible tab's dimension is fetched, which is also the performance story —
+ * half the dimensions heap-scan the ledger, and a tab nobody opens never pays for one.
+ */
+export function UsageMonitoring({ organizationId, workspaceId }: UsageMonitoringProps) {
+ const router = useRouter()
+ const { window, tab, workspace, preset, startDate, endDate, periodLabel, setState } =
+ useUsageWindow()
+ const [datePickerOpen, setDatePickerOpen] = useState(false)
+ /** The member whose credit limit is being edited, or null when the modal is closed. */
+ const [creditsTarget, setCreditsTarget] = useState(null)
+
+ const isOverview = tab === USAGE_OVERVIEW_TAB
+ /**
+ * A selected workspace turns the Workspaces tab into that workspace's workflows —
+ * but only once the id resolves against the loaded list. A bookmarked id for a
+ * deleted workspace, or one belonging to another organization, would otherwise open
+ * a detail view with an untitled header and empty sections. Falling back to the
+ * list is the rule for every deep-linked entity id (`sim-url-state.md`); the
+ * lingering param is harmless.
+ */
+ const isWorkspaceSelected = tab === 'workspace' && Boolean(workspace)
+
+ /**
+ * Per-member caps are hosted-only: the usage-limit route 404s where Sim does not
+ * own billing, and there is no enforcement to hang a cap off. This panel is the
+ * one organization surface a self-hosted enterprise can reach — Members is
+ * `requiresHosted` with no self-hosted override — so without this the menu would
+ * offer an action that could only fail.
+ */
+ const canManageCredits = tab === 'member' && isHosted
+
+ const summary = useOrganizationUsageSummary(organizationId, window)
+ /**
+ * Kept alive in the drill-down purely to name it. The rule is to store the id and
+ * derive the entity from the loaded list; arriving by click serves this from cache,
+ * and arriving by deep link fetches it once.
+ */
+ const workspaceList = useOrganizationUsageBreakdown(organizationId, window, 'workspace', {
+ enabled: isWorkspaceSelected,
+ })
+ const workspaceName = workspaceList.data?.rows.find((row) => row.id === workspace)?.label
+ /**
+ * Resolved, not merely present — and only once the list has actually loaded, so a
+ * deep link does not flash the Workspaces tab before its own detail view.
+ */
+ const isWorkspaceDetail =
+ isWorkspaceSelected && (workspaceList.isLoading || workspaceName !== undefined)
+
+ const dimension: UsageBreakdownDimension = isOverview
+ ? 'source'
+ : isWorkspaceDetail
+ ? 'workflow'
+ : (tab as UsageBreakdownDimension)
+
+ const breakdown = useOrganizationUsageBreakdown(organizationId, window, dimension, {
+ ...(isWorkspaceDetail && workspace ? { workspaceId: workspace } : {}),
+ })
+ const workspaceSources = useOrganizationUsageBreakdown(organizationId, window, 'source', {
+ enabled: isWorkspaceDetail,
+ ...(workspace ? { workspaceId: workspace } : {}),
+ })
+ // Already cached by Members and Billing, so the meter costs nothing extra and
+ // cannot report a different allowance than they do.
+ const billing = useOrganizationBilling(organizationId)
+
+ const handlePeriodChange = (value: string) => {
+ if (value === 'custom') {
+ setDatePickerOpen(true)
+ return
+ }
+ void setState({ preset: value as typeof preset, startDate: null, endDate: null })
+ }
+
+ const handleDateRangeApply = (nextStart: string, nextEnd: string) => {
+ /**
+ * Refuse an over-long range here rather than committing it and letting all four
+ * reads fail. The server still enforces the cap — this is the same rule stated
+ * where the user can act on it, with the picker left open on the selection that
+ * needs changing.
+ */
+ const spanDays = Math.ceil(
+ (new Date(nextEnd).getTime() - new Date(nextStart).getTime()) / DAY_MS
+ )
+ if (spanDays + 1 > MAX_CUSTOM_RANGE_DAYS) {
+ toast.error(`Select a range of ${MAX_CUSTOM_RANGE_DAYS} days or fewer`)
+ return
+ }
+ void setState({ preset: 'custom', startDate: nextStart, endDate: nextEnd })
+ setDatePickerOpen(false)
+ }
+
+ const handleExport = async () => {
+ // The organization is the path segment below; the query no longer carries a
+ // second copy of it.
+ const params = new URLSearchParams({
+ preset: window.preset,
+ timezone: window.timezone,
+ })
+ if (window.startDate) params.set('startDate', window.startDate)
+ if (window.endDate) params.set('endDate', window.endDate)
+
+ /**
+ * Wrapped because the action is fire-and-forget: `onSelect` cannot await this, so
+ * a rejection — a dropped connection, a blob read that fails — became an unhandled
+ * promise and the button appeared to do nothing at all.
+ */
+ try {
+ // boundary-raw-fetch: downloads a CSV blob and reads X-Export-Truncated before saving — a plain anchor navigation can do neither
+ const response = await fetch(
+ `/api/organizations/${organizationId}/usage/export?${params.toString()}`
+ )
+ if (!response.ok) {
+ toast.error('Failed to export usage')
+ return
+ }
+ if (response.headers.get('X-Export-Truncated') === '1') {
+ toast.info('Export truncated — narrow the date range to see everything')
+ }
+
+ const blob = await response.blob()
+ const url = URL.createObjectURL(blob)
+ const link = document.createElement('a')
+ link.href = url
+ link.download = `organization-usage-${new Date().toISOString().slice(0, 10)}.csv`
+ document.body.appendChild(link)
+ link.click()
+ link.remove()
+ URL.revokeObjectURL(url)
+ } catch {
+ toast.error('Failed to export usage')
+ }
+ }
+
+ /**
+ * The drill-down is a detail view, so it takes over the header: a back chip out of
+ * it, and the one action that belongs to a workspace rather than the organization.
+ */
+ if (isWorkspaceDetail && workspace) {
+ return (
+ void setState({ workspace: null }),
+ }}
+ title={workspaceName ?? 'Workspace usage'}
+ actions={[
+ {
+ text: 'Open logs',
+ onSelect: () => router.push(`/workspace/${workspace}/logs`),
+ },
+ ]}
+ >
+ {/*
+ Sources first, because in most workspaces the majority of usage is Chat
+ rather than workflow runs — and a workflow list alone hid that behind a
+ single unexplained row. Sources reconciles to the workspace total; Workflows
+ is explicitly the workflow-run subset of it.
+ */}
+
+
+
+
+
+
+
+ )
+ }
+
+ return (
+ router.push(`/workspace/${workspaceId}/settings/usage/events`),
+ },
+ {
+ text: 'Export',
+ icon: Download,
+ onSelect: () => void handleExport(),
+ },
+ ]}
+ >
+
+
void setState({ tab: value as UsageTab, workspace: null })}
+ />
+
+ {/* ChipCombobox (Radix Popover, non-modal), not ChipSelect (Radix
+ DropdownMenu, modal by default) — a modal trigger closing in the
+ same tick that opens the Calendar popover below traps it behind
+ the modal's focus lock, so "Custom range" silently does nothing. */}
+
{periodLabel}
+ }
+ align='end'
+ />
+ {
+ if (!isOpen) setDatePickerOpen(false)
+ }}
+ >
+
+
+ {/*
+ No `showTime`: the panel buckets by calendar day, so a time of day is
+ precision it cannot render. It also emitted the end bound as an
+ inclusive `…T23:59:59` local wall time, which the window resolver then
+ treated as a midnight and pushed a further 24h — every custom range
+ covered an extra day, and a legal 92-day pick measured 93 and was
+ rejected. Bare `YYYY-MM-DD` bounds parse as UTC midnight, matching the
+ rest of the window logic.
+ */}
+ setDatePickerOpen(false)}
+ />
+
+
+
+
+
+ {isOverview ? (
+ <>
+ {/*
+ The allowance is a per-billing-period figure, so it is only comparable
+ to the current period's total. Against a rolling window or a custom
+ range it measures a different span than the limit covers — a 30-day
+ window spanning two periods could read "Over limit" while neither
+ period was — so those windows show the figure without an allowance.
+ */}
+
+ {/*
+ "What kind of work was this?" belongs beside the total it explains, not
+ behind a tab — it is the second half of the same sentence.
+ */}
+
+
+
+ >
+ ) : (
+
+ void setState({ workspace: row.id }) }
+ : {})}
+ {...(canManageCredits
+ ? {
+ rowActions: (row) => [
+ {
+ label: 'Manage credits',
+ onSelect: () => setCreditsTarget({ userId: row.id, name: row.label }),
+ },
+ ],
+ }
+ : {})}
+ />
+
+ )}
+
+ {/*
+ The same modal the Members settings page opens, driven by the same hooks —
+ setting a cap here and there is one implementation, not two.
+ */}
+ {
+ if (!open) setCreditsTarget(null)
+ }}
+ organizationId={organizationId}
+ member={creditsTarget}
+ />
+
+ )
+}
diff --git a/apps/sim/ee/organization-usage/components/usage-summary.tsx b/apps/sim/ee/organization-usage/components/usage-summary.tsx
new file mode 100644
index 00000000000..43cb6a1ac31
--- /dev/null
+++ b/apps/sim/ee/organization-usage/components/usage-summary.tsx
@@ -0,0 +1,84 @@
+'use client'
+
+import { Badge } from '@sim/emcn'
+import { BarChart } from '@/components/charts'
+import type { OrganizationUsageSummary } from '@/lib/api/contracts/organization-usage'
+import { formatCreditsLabel } from '@/lib/billing/credits/conversion'
+import { SettingsEmptyState } from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state'
+
+/** Consumption, matching the seat meter's indicator rather than an outcome colour. */
+const USAGE_SERIES_COLOR = 'var(--indicator-seat-filled)'
+
+interface UsageSummaryProps {
+ summary?: OrganizationUsageSummary
+ /** Pooled allowance in credits, from the organization's billing data. `null` when uncapped. */
+ limitCredits?: number | null
+ isLoading: boolean
+ isError: boolean
+}
+
+function percentDelta(current: number, previous: number): number | null {
+ if (previous <= 0) return null
+ return ((current - previous) / previous) * 100
+}
+
+export function UsageSummary({ summary, limitCredits, isLoading, isError }: UsageSummaryProps) {
+ if (isError) {
+ return (
+
+ Couldn't load usage.
+
+ )
+ }
+ if (isLoading || !summary) {
+ return Loading usage…
+ }
+
+ const used = summary.totals.credits
+ const delta = summary.previousTotals ? percentDelta(used, summary.previousTotals.credits) : null
+ const hasLimit = limitCredits != null && limitCredits > 0
+ const isOverLimit = hasLimit && used > limitCredits
+
+ return (
+
+ {/*
+ One line, and the allowance sits beside the figure rather than under it —
+ restating "4,958 credits used" below a "4,958 credits" headline said the same
+ number twice and read as a rendering bug.
+ */}
+
+
+ {formatCreditsLabel(used)}
+
+ {hasLimit && (
+ // Bare number, not `formatCreditsLabel`: the headline beside it already
+ // names the unit, and "4,958 credits of 200,000 credits" says it twice.
+
+ of {limitCredits.toLocaleString()}
+
+ )}
+ {delta !== null && (
+ 0 ? 'amber' : 'gray-secondary'} size='sm'>
+ {`${delta > 0 ? '↑' : '↓'} ${Math.abs(delta).toFixed(0)}% vs last period`}
+
+ )}
+ {isOverLimit && (
+
+ Over limit
+
+ )}
+
+
+
({
+ timestamp: point.timestamp,
+ value: point.credits,
+ }))}
+ label=''
+ color={USAGE_SERIES_COLOR}
+ unit='credits'
+ height={160}
+ />
+
+ )
+}
diff --git a/apps/sim/ee/organization-usage/constants.ts b/apps/sim/ee/organization-usage/constants.ts
new file mode 100644
index 00000000000..4a96fd042b7
--- /dev/null
+++ b/apps/sim/ee/organization-usage/constants.ts
@@ -0,0 +1,74 @@
+import type { ComboboxOption } from '@sim/emcn'
+import {
+ USAGE_WINDOW_PRESETS,
+ type UsageBreakdownDimension,
+ type UsageWindowPreset,
+} from '@/lib/api/contracts/organization-usage'
+
+/**
+ * Total by construction, so resolving a preset's label needs no fallback — a `Record`
+ * over the union cannot miss a case, where a `find` over an array always can.
+ */
+export const PERIOD_LABELS: Record = {
+ 'current-period': 'Current period',
+ 'previous-period': 'Previous period',
+ '7d': 'Last 7 days',
+ '30d': 'Last 30 days',
+ custom: 'Custom range',
+}
+
+export const PERIOD_OPTIONS: ComboboxOption[] = USAGE_WINDOW_PRESETS.map((preset) => ({
+ value: preset,
+ label: PERIOD_LABELS[preset],
+}))
+
+export const USAGE_OVERVIEW_TAB = 'overview' as const
+export type UsageTab = typeof USAGE_OVERVIEW_TAB | 'member' | 'workspace' | 'model' | 'byok'
+
+/**
+ * The panel reads as one question per tab, in the order an admin asks them:
+ * how much (Overview, which also answers *what kind* via its source mix), then who,
+ * then where, then on what.
+ *
+ * Workflows is deliberately not a tab. A workflow is only meaningful inside its
+ * workspace, and a flat org-wide workflow list is dominated by a bucket of usage that
+ * has no workflow at all — so it lives as the Workspaces drill-down instead.
+ */
+export const USAGE_TAB_ORDER: readonly UsageTab[] = [
+ USAGE_OVERVIEW_TAB,
+ 'member',
+ 'workspace',
+ 'model',
+ 'byok',
+]
+
+export const USAGE_TAB_LABELS: Record = {
+ overview: 'Overview',
+ member: 'Members',
+ workspace: 'Workspaces',
+ model: 'Models',
+ byok: 'BYOK',
+}
+
+/** Section heading per view, so a list is never an unlabelled slab of rows. */
+export const USAGE_SECTION_LABELS: Record = {
+ member: 'Members',
+ workspace: 'Workspaces',
+ workflow: 'Workflows',
+ model: 'Models',
+ byok: 'BYOK',
+ source: 'Sources',
+}
+
+/** Empty-state copy per view, so a quiet dimension says which one it means. */
+export const USAGE_TAB_EMPTY_COPY: Record = {
+ member: 'No member used credits in this period.',
+ workspace: 'No workspace used credits in this period.',
+ workflow: 'No workflow ran in this workspace in this period.',
+ model: 'No models ran in this period.',
+ byok: 'No usage on your own provider keys in this period.',
+ source: 'Nothing consumed credits in this period.',
+}
+
+export const DEFAULT_USAGE_PRESET = 'current-period' as const
+export const DEFAULT_USAGE_TAB = USAGE_OVERVIEW_TAB
diff --git a/apps/sim/ee/organization-usage/hooks/use-usage-window.ts b/apps/sim/ee/organization-usage/hooks/use-usage-window.ts
new file mode 100644
index 00000000000..212c44ba596
--- /dev/null
+++ b/apps/sim/ee/organization-usage/hooks/use-usage-window.ts
@@ -0,0 +1,97 @@
+'use client'
+
+import { useMemo } from 'react'
+import { useQueryStates } from 'nuqs'
+import {
+ MAX_CUSTOM_RANGE_DAYS,
+ type UsageWindowPreset,
+} from '@/lib/api/contracts/organization-usage'
+import { formatDateShort } from '@/lib/core/utils/date-display'
+import { getBrowserTimezone } from '@/lib/core/utils/timezone'
+import { DEFAULT_USAGE_PRESET, PERIOD_LABELS } from '@/ee/organization-usage/constants'
+import {
+ organizationUsageParsers,
+ organizationUsageUrlKeys,
+} from '@/ee/organization-usage/search-params'
+import type { OrganizationUsageWindowKey } from '@/hooks/queries/utils/organization-usage-keys'
+
+const DAY_MS = 24 * 60 * 60 * 1000
+
+/** A bare `YYYY-MM-DD` that survives a calendar round-trip — the contract's rule, verbatim. */
+function isCalendarDate(value: string | null): value is string {
+ if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return false
+ return new Date(`${value}T00:00:00.000Z`).toISOString().slice(0, 10) === value
+}
+
+/**
+ * Every rule the window resolver enforces, checked here too.
+ *
+ * The server refuses an unreal date, an inverted pair, and a span past the cap — each
+ * as a 400. A deep link carrying any of them would otherwise be marked "resolved" and
+ * fail all four queries on the page, which is a worse outcome than the fallback this
+ * guard exists to provide. Duplicated deliberately, and narrowly: these are the three
+ * conditions that turn a link into an error rather than into different data.
+ */
+function isUsableCustomRange(start: string | null, end: string | null): boolean {
+ if (!isCalendarDate(start) || !isCalendarDate(end)) return false
+ const from = new Date(`${start}T00:00:00.000Z`).getTime()
+ const to = new Date(`${end}T00:00:00.000Z`).getTime()
+ if (to < from) return false
+ return Math.round((to - from) / DAY_MS) + 1 <= MAX_CUSTOM_RANGE_DAYS
+}
+
+/**
+ * The panel's URL state, resolved into the window every query is keyed on.
+ *
+ * A `custom` preset missing either bound falls back to the default rather than
+ * querying unbounded — the same partial-deep-link guard audit-logs uses.
+ */
+export function useUsageWindow() {
+ const [state, setState] = useQueryStates(organizationUsageParsers, organizationUsageUrlKeys)
+ const timezone = getBrowserTimezone()
+
+ /**
+ * Both bounds present, and a range the API will actually accept.
+ *
+ * Every condition the window resolver refuses with a 400 — an unreal date, an
+ * inverted pair, a span past the cap — has to be checked here too, or a bookmarked
+ * link carrying one is marked resolved and fails all four queries on the page. The
+ * fallback exists precisely so a bad link degrades to the default window instead.
+ */
+ const isResolvedCustom =
+ state.preset === 'custom' && isUsableCustomRange(state.startDate, state.endDate)
+ const preset: UsageWindowPreset =
+ state.preset === 'custom' && !isResolvedCustom ? DEFAULT_USAGE_PRESET : state.preset
+
+ const window = useMemo(
+ () => ({
+ preset,
+ ...(isResolvedCustom
+ ? { startDate: state.startDate ?? undefined, endDate: state.endDate ?? undefined }
+ : {}),
+ timezone,
+ }),
+ [preset, isResolvedCustom, state.startDate, state.endDate, timezone]
+ )
+
+ const periodLabel = isResolvedCustom
+ ? `${formatDateShort(state.startDate as string)} - ${formatDateShort(state.endDate as string)}`
+ : PERIOD_LABELS[preset]
+
+ return {
+ window,
+ tab: state.tab,
+ workspace: state.workspace,
+ /**
+ * The *resolved* preset, not the raw URL value. A partial custom deep link
+ * queries the current period, so surfacing `state.preset` left the picker
+ * reading "Custom range" over data that was not custom — and the allowance
+ * gate, which keys on `current-period`, disagreed with the window too.
+ */
+ preset,
+ startDate: state.startDate,
+ endDate: state.endDate,
+ periodLabel,
+ setState,
+ }
+}
diff --git a/apps/sim/ee/organization-usage/search-params.ts b/apps/sim/ee/organization-usage/search-params.ts
new file mode 100644
index 00000000000..576db83fc4b
--- /dev/null
+++ b/apps/sim/ee/organization-usage/search-params.ts
@@ -0,0 +1,40 @@
+import { parseAsString, parseAsStringLiteral } from 'nuqs/server'
+import { USAGE_WINDOW_PRESETS } from '@/lib/api/contracts/organization-usage'
+import { parseAsDateString } from '@/app/workspace/[workspaceId]/logs/search-params'
+import {
+ DEFAULT_USAGE_PRESET,
+ DEFAULT_USAGE_TAB,
+ USAGE_TAB_ORDER,
+} from '@/ee/organization-usage/constants'
+
+/**
+ * URL state for the organization usage panel.
+ *
+ * `startDate`/`endDate` are deliberately nullable (no `.withDefault`): they exist only
+ * while `preset` is `custom`. Every other preset derives its window server-side from
+ * the organization's subscription period, so a default here would be meaningless — and
+ * worse, would silently pin the window to a stale date.
+ */
+export const organizationUsageParsers = {
+ preset: parseAsStringLiteral(USAGE_WINDOW_PRESETS).withDefault(DEFAULT_USAGE_PRESET),
+ startDate: parseAsDateString,
+ endDate: parseAsDateString,
+ tab: parseAsStringLiteral(USAGE_TAB_ORDER).withDefault(DEFAULT_USAGE_TAB),
+ /**
+ * Nullable by design: only the id is stored, and the detail view opens only once it
+ * resolves against the loaded list — a stale id from an old link falls back to the
+ * list rather than rendering an empty drill-down.
+ */
+ workspace: parseAsString,
+} as const
+
+/** Filter view-state: clean URLs, no back-stack churn, kebab-case URL keys. */
+export const organizationUsageUrlKeys = {
+ history: 'replace',
+ shallow: true,
+ clearOnDefault: true,
+ urlKeys: {
+ startDate: 'start-date',
+ endDate: 'end-date',
+ },
+} as const
diff --git a/apps/sim/hooks/queries/organization-usage.ts b/apps/sim/hooks/queries/organization-usage.ts
new file mode 100644
index 00000000000..ef19c5f5fe2
--- /dev/null
+++ b/apps/sim/hooks/queries/organization-usage.ts
@@ -0,0 +1,114 @@
+'use client'
+
+import { keepPreviousData, useInfiniteQuery, useQuery } from '@tanstack/react-query'
+import { requestJson } from '@/lib/api/client/request'
+import {
+ getOrganizationUsageBreakdownContract,
+ getOrganizationUsageSummaryContract,
+ listOrganizationUsageEventsContract,
+ type OrganizationUsageBreakdown,
+ type OrganizationUsageEventPage,
+ type OrganizationUsageSummary,
+ type UsageBreakdownDimension,
+} from '@/lib/api/contracts/organization-usage'
+import {
+ type OrganizationUsageWindowKey,
+ organizationUsageKeys,
+} from '@/hooks/queries/utils/organization-usage-keys'
+
+export const ORGANIZATION_USAGE_SUMMARY_STALE_TIME = 60 * 1000
+/**
+ * Longer than the summary: a ranking does not move meaningfully within a minute, and
+ * three of the five dimensions heap-scan the ledger.
+ */
+export const ORGANIZATION_USAGE_BREAKDOWN_STALE_TIME = 5 * 60 * 1000
+export const ORGANIZATION_USAGE_EVENTS_STALE_TIME = 30 * 1000
+
+const EVENTS_PAGE_SIZE = 50
+
+export function useOrganizationUsageSummary(
+ organizationId: string | undefined,
+ window: OrganizationUsageWindowKey
+) {
+ return useQuery({
+ queryKey: organizationUsageKeys.summary(organizationId ?? '', window),
+ queryFn: ({ signal }): Promise =>
+ requestJson(getOrganizationUsageSummaryContract, {
+ params: { id: organizationId as string },
+ query: { ...window },
+ signal,
+ }),
+ enabled: Boolean(organizationId),
+ staleTime: ORGANIZATION_USAGE_SUMMARY_STALE_TIME,
+ // Changing the period should dim the current figures rather than blank them.
+ placeholderData: keepPreviousData,
+ })
+}
+
+interface UseBreakdownOptions {
+ limit?: number
+ /** The panel passes the selected tab, so only the visible list is ever fetched. */
+ enabled?: boolean
+ /** Narrows to one workspace, for the Workspaces drill-down. */
+ workspaceId?: string
+}
+
+export function useOrganizationUsageBreakdown(
+ organizationId: string | undefined,
+ window: OrganizationUsageWindowKey,
+ dimension: UsageBreakdownDimension,
+ options: UseBreakdownOptions = {}
+) {
+ const limit = options.limit ?? 10
+ const { workspaceId } = options
+ return useQuery({
+ queryKey: organizationUsageKeys.breakdown(
+ organizationId ?? '',
+ window,
+ dimension,
+ limit,
+ workspaceId
+ ),
+ queryFn: ({ signal }): Promise =>
+ requestJson(getOrganizationUsageBreakdownContract, {
+ params: { id: organizationId as string },
+ query: {
+ ...window,
+ dimension,
+ limit,
+ ...(workspaceId ? { workspaceId } : {}),
+ },
+ signal,
+ }),
+ enabled: Boolean(organizationId) && (options.enabled ?? true),
+ staleTime: ORGANIZATION_USAGE_BREAKDOWN_STALE_TIME,
+ // Deliberately no keepPreviousData: a stale ranking under a new group label reads
+ // as wrong data, which is worse than a brief skeleton.
+ })
+}
+
+export function useOrganizationUsageEvents(
+ organizationId: string | undefined,
+ window: OrganizationUsageWindowKey,
+ sources: string[] = []
+) {
+ return useInfiniteQuery({
+ queryKey: organizationUsageKeys.events(organizationId ?? '', window, sources),
+ queryFn: ({ pageParam, signal }): Promise =>
+ requestJson(listOrganizationUsageEventsContract, {
+ params: { id: organizationId as string },
+ query: {
+ ...window,
+ ...(sources.length ? { source: sources } : {}),
+ limit: EVENTS_PAGE_SIZE,
+ cursor: pageParam,
+ },
+ signal,
+ }),
+ initialPageParam: undefined as string | undefined,
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
+ enabled: Boolean(organizationId),
+ staleTime: ORGANIZATION_USAGE_EVENTS_STALE_TIME,
+ placeholderData: keepPreviousData,
+ })
+}
diff --git a/apps/sim/hooks/queries/utils/organization-usage-keys.ts b/apps/sim/hooks/queries/utils/organization-usage-keys.ts
new file mode 100644
index 00000000000..85077abff41
--- /dev/null
+++ b/apps/sim/hooks/queries/utils/organization-usage-keys.ts
@@ -0,0 +1,45 @@
+import type {
+ UsageBreakdownDimension,
+ UsageWindowPreset,
+} from '@/lib/api/contracts/organization-usage'
+
+/**
+ * Query keys for organization usage.
+ *
+ * Not a `'use client'` module: a key factory imported by a server-evaluated
+ * prefetch would become a client reference and throw when called.
+ *
+ * The keys nest under `['organizations', 'detail', orgId]` deliberately, so the
+ * existing `invalidateQueries({ queryKey: organizationKeys.detail(orgId) })` calls —
+ * after a member's credit limit changes, for instance — sweep usage too.
+ */
+
+export interface OrganizationUsageWindowKey {
+ preset: UsageWindowPreset
+ startDate?: string
+ endDate?: string
+ timezone: string
+}
+
+export const organizationUsageKeys = {
+ all: (organizationId: string) => ['organizations', 'detail', organizationId, 'usage'] as const,
+ summary: (organizationId: string, window: OrganizationUsageWindowKey) =>
+ [...organizationUsageKeys.all(organizationId), 'summary', window] as const,
+ breakdowns: (organizationId: string, window: OrganizationUsageWindowKey) =>
+ [...organizationUsageKeys.all(organizationId), 'breakdown', window] as const,
+ breakdown: (
+ organizationId: string,
+ window: OrganizationUsageWindowKey,
+ dimension: UsageBreakdownDimension,
+ limit: number,
+ workspaceId?: string
+ ) =>
+ [
+ ...organizationUsageKeys.breakdowns(organizationId, window),
+ dimension,
+ limit,
+ workspaceId ?? '',
+ ] as const,
+ events: (organizationId: string, window: OrganizationUsageWindowKey, sources: string[]) =>
+ [...organizationUsageKeys.all(organizationId), 'events', window, sources] as const,
+}
diff --git a/apps/sim/lib/api/contracts/organization-usage.test.ts b/apps/sim/lib/api/contracts/organization-usage.test.ts
new file mode 100644
index 00000000000..91b25dbeac1
--- /dev/null
+++ b/apps/sim/lib/api/contracts/organization-usage.test.ts
@@ -0,0 +1,70 @@
+/**
+ * @vitest-environment node
+ */
+import { describe, expect, it } from 'vitest'
+import { organizationUsageEventsQuerySchema } from '@/lib/api/contracts/organization-usage'
+
+/** The shared window fields every usage contract extends, exercised through one of them. */
+function parseWindow(input: Record) {
+ return organizationUsageEventsQuerySchema.safeParse({ preset: 'custom', ...input })
+}
+
+describe('organization usage window contract', () => {
+ it('accepts a real calendar date', () => {
+ expect(parseWindow({ startDate: '2026-08-01', endDate: '2026-08-31' }).success).toBe(true)
+ })
+
+ it('refuses a date that does not exist', () => {
+ // `Date.parse` accepts this and rolls it forward to March 2, so a request for
+ // February would otherwise be answered about March without saying so.
+ expect(parseWindow({ startDate: '2026-02-30' }).success).toBe(false)
+ })
+
+ it('refuses a parseable non-date such as a bare month', () => {
+ // `new Date('2026-08')` is August 1. Accepting it returned a window the caller
+ // never asked for, with nothing to indicate the value had been reinterpreted.
+ expect(parseWindow({ startDate: '2026-08' }).success).toBe(false)
+ })
+
+ it('refuses anything after the date, including a well-formed datetime', () => {
+ // The picker sends bare dates only, and every looser rule broke differently:
+ // `…Tgarbage` parsed to an Invalid Date that made the resolver throw from
+ // `toISOString` (a 500 for a bad query string), and an offset datetime validated
+ // on its date part while the resolver read a different UTC day off the whole
+ // value — so the range shown and the range queried disagreed.
+ expect(parseWindow({ startDate: '2026-08-01Tgarbage' }).success).toBe(false)
+ expect(parseWindow({ startDate: '2026-08-01T00:00:00+05:00' }).success).toBe(false)
+ expect(parseWindow({ startDate: '2026-02-30T00:00:00' }).success).toBe(false)
+ })
+
+ it('refuses an empty date but allows an absent one', () => {
+ // The picker clears the param rather than blanking it, so `?start-date=` is a
+ // malformed request — and treating it as absent silently answered about the
+ // current period instead of the range the caller named.
+ expect(parseWindow({ startDate: '' }).success).toBe(false)
+ expect(parseWindow({}).success).toBe(true)
+ })
+
+ it('treats an empty limit as omitted rather than as zero', () => {
+ // `z.coerce.number()` turns `''` into `0`, which then fails `.min(1)` — so a
+ // client serializing an unset filter got a 400 instead of the declared default.
+ const parsed = parseWindow({ limit: '' })
+ expect(parsed.success).toBe(true)
+ if (parsed.success) expect(parsed.data.limit).toBe(50)
+ })
+
+ it('normalizes a single source to a one-item array', () => {
+ // One selected filter arrives as a scalar, which a bare `z.array` rejected.
+ const parsed = parseWindow({ source: 'workflow' })
+ expect(parsed.success).toBe(true)
+ if (parsed.success) expect(parsed.data.source).toEqual(['workflow'])
+ })
+
+ it('refuses an unknown source instead of matching nothing', () => {
+ expect(parseWindow({ source: 'not-a-source' }).success).toBe(false)
+ })
+
+ it('refuses a timezone the runtime does not recognize', () => {
+ expect(parseWindow({ timezone: 'Mars/Olympus_Mons' }).success).toBe(false)
+ })
+})
diff --git a/apps/sim/lib/api/contracts/organization-usage.ts b/apps/sim/lib/api/contracts/organization-usage.ts
new file mode 100644
index 00000000000..658a9f040c1
--- /dev/null
+++ b/apps/sim/lib/api/contracts/organization-usage.ts
@@ -0,0 +1,271 @@
+import { z } from 'zod'
+import { organizationIdSchema, workspaceIdSchema } from '@/lib/api/contracts/primitives'
+import { defineRouteContract } from '@/lib/api/contracts/types'
+import { INTERNAL_USAGE_LOG_SOURCES } from '@/lib/billing/usage-sources'
+import { isValidTimezone } from '@/lib/core/utils/timezone'
+
+/**
+ * Organization usage monitoring (enterprise).
+ *
+ * Everything on the wire is denominated in **credits**, never dollars: the ledger
+ * stores dollars and the use cases convert at this boundary, matching every other
+ * usage surface in the product.
+ */
+
+export const USAGE_WINDOW_PRESETS = [
+ 'current-period',
+ 'previous-period',
+ '7d',
+ '30d',
+ 'custom',
+] as const
+export const usageWindowPresetSchema = z.enum(USAGE_WINDOW_PRESETS).default('current-period')
+export type UsageWindowPreset = z.output
+
+export const USAGE_BREAKDOWN_DIMENSIONS = [
+ 'member',
+ 'workspace',
+ 'workflow',
+ 'model',
+ 'byok',
+ 'source',
+] as const
+export const usageBreakdownDimensionSchema = z.enum(USAGE_BREAKDOWN_DIMENSIONS)
+export type UsageBreakdownDimension = z.output
+
+/**
+ * The longest custom range the ledger will scan. Declared on the contract so the
+ * picker states the same limit the window resolver enforces, rather than the client
+ * discovering it from a rejected request.
+ */
+export const MAX_CUSTOM_RANGE_DAYS = 92
+
+/**
+ * A bare `YYYY-MM-DD` calendar date, and nothing else.
+ *
+ * Strict on purpose. The picker sends only bare dates — it has no time component —
+ * and every looser rule tried here has been wrong in a different way:
+ *
+ * - `Date.parse` alone accepts `2026-02-30` and rolls it forward, so February was
+ * answered about March. The round-trip below is what makes this a *calendar*
+ * check: a day that does not survive re-serialization never existed.
+ * - Validating only a `YYYY-MM-DD` prefix let `2026-08` through as August 1, and
+ * `2026-08-01Tgarbage` through as an `Invalid Date` that made the window resolver
+ * throw from `toISOString` — a 500 for a malformed query string.
+ * - A datetime with an offset would validate on its date part while the resolver
+ * read a different UTC day off the full value, so the range shown and the range
+ * queried could disagree.
+ *
+ * Accepting only the one form the client actually sends removes all three at once.
+ */
+const isoDateSchema = z
+ .string()
+ .optional()
+ .refine(
+ (value) => {
+ /*
+ Absent is allowed; empty is not. A missing bound is a real state — the picker
+ clears the param rather than blanking it — and the resolver falls back to the
+ current period for it. An explicit `?start-date=` is a malformed request, and
+ treating it as absent silently answered about a different window than the one
+ asked for.
+
+ Deliberately unlike `usageLimitSchema`, which does coerce `''` to its default:
+ that field declares a default, so omission has a documented meaning. These
+ bounds have none — omitting one changes which period you get.
+ */
+ if (value === undefined) return true
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false
+ return new Date(`${value}T00:00:00.000Z`).toISOString().slice(0, 10) === value
+ },
+ { message: 'Expected a calendar date in YYYY-MM-DD form, such as 2026-08-01' }
+ )
+
+/**
+ * A page size that treats an empty or absent parameter as omitted.
+ *
+ * `z.coerce.number()` turns `''` into `0`, which then fails `.min(1)` — so a client
+ * that serializes an unset filter as `?limit=` got a 400 instead of the default the
+ * schema declares. Explicit numeric values still validate normally.
+ */
+function usageLimitSchema(max: number, fallback: number) {
+ return z.preprocess(
+ (value) => (value === '' || value === null ? undefined : value),
+ z.coerce.number().int().min(1).max(max).default(fallback)
+ )
+}
+
+/**
+ * Shared by all four contracts so the four surfaces cannot describe different
+ * windows — a mismatch here is how the tiles and the event log would disagree.
+ */
+const organizationUsageWindowQuerySchema = z.object({
+ /*
+ No `organizationId` here. The organization is the path parameter, and that is the
+ one every handler authorizes and reads. Accepting a second copy in the query meant
+ a request could name one organization and be answered about another — not an
+ authorization hole, since `params.id` is what gets checked, but an API that reads
+ as though the query mattered.
+ */
+ preset: usageWindowPresetSchema,
+ startDate: isoDateSchema,
+ endDate: isoDateSchema,
+ /**
+ * IANA name; bucket boundaries are the viewer's calendar days.
+ *
+ * Validated here rather than only at the SQL boundary. `assertValidTimezone` is a
+ * hard gate — the value reaches `AT TIME ZONE`, which takes an identifier and not
+ * a bound parameter — but it throws a plain `Error`, which surfaced as a 500 for
+ * what is an ordinary bad query param. Rejecting it as a contract violation makes
+ * it a 400 with a usable message and leaves that gate as the backstop it is.
+ */
+ timezone: z
+ .string()
+ .min(1, 'timezone cannot be empty')
+ .refine(isValidTimezone, 'Expected an IANA timezone such as America/Los_Angeles')
+ .default('UTC'),
+})
+
+export const organizationUsageSummaryQuerySchema = organizationUsageWindowQuerySchema
+export type OrganizationUsageSummaryQuery = z.input
+
+export const organizationUsageBreakdownQuerySchema = organizationUsageWindowQuerySchema.extend({
+ dimension: usageBreakdownDimensionSchema,
+ /** Narrows the breakdown to one workspace, for the Workspaces drill-down. */
+ workspaceId: workspaceIdSchema.optional(),
+ limit: usageLimitSchema(50, 10),
+})
+export type OrganizationUsageBreakdownQuery = z.input
+
+/**
+ * Ledger sources, as an enum rather than free strings.
+ *
+ * Two problems this closes. A single selected source arrives on the wire as one
+ * scalar, not a one-item array, so an `z.array(...)` alone rejected the commonest
+ * filter outright — hence the union and normalization. And an unrecognized value
+ * used to survive validation and reach the query as an unchecked cast, where it
+ * matched nothing and returned an empty page that looked like "no usage" rather
+ * than a bad request.
+ */
+const usageLogSourceFilterSchema = z
+ .union([z.string(), z.array(z.string())])
+ .transform((value) => (Array.isArray(value) ? value : [value]))
+ .pipe(z.array(z.enum(INTERNAL_USAGE_LOG_SOURCES)).max(20))
+
+export const organizationUsageEventsQuerySchema = organizationUsageWindowQuerySchema.extend({
+ source: usageLogSourceFilterSchema.optional(),
+ limit: usageLimitSchema(100, 50),
+ cursor: z.string().min(1).optional(),
+})
+export type OrganizationUsageEventsQuery = z.input
+
+export const organizationUsageExportQuerySchema = organizationUsageEventsQuerySchema.omit({
+ limit: true,
+ cursor: true,
+})
+export type OrganizationUsageExportQuery = z.input
+
+/** Only the headline figure — see `readUsageTotals` for why nothing else lives here. */
+const usageTotalsSchema = z.object({
+ credits: z.number(),
+})
+
+const usageSeriesPointSchema = z.object({
+ timestamp: z.string(),
+ credits: z.number(),
+ events: z.number().int(),
+})
+
+export const organizationUsageSummaryResponseSchema = z.object({
+ window: z.object({
+ start: z.string(),
+ end: z.string(),
+ source: z.enum(['reporting', 'stripe', 'default', 'range']),
+ }),
+ bucket: z.enum(['day', 'week', 'month']),
+ totals: usageTotalsSchema,
+ /** `null` when the prior window is not exactly derivable — no delta beats a wrong one. */
+ previousTotals: usageTotalsSchema.nullable(),
+ series: z.array(usageSeriesPointSchema),
+})
+export type OrganizationUsageSummary = z.output
+
+export const organizationUsageBreakdownRowSchema = z.object({
+ id: z.string(),
+ label: z.string(),
+ credits: z.number(),
+ events: z.number().int(),
+ /** 0..1 of the window total, not of the visible rows. */
+ share: z.number().min(0).max(1),
+ /** Model dimensions only — resolved server-side so the client needs no model registry. */
+ providerId: z.string().optional(),
+ /** Model dimensions only; BYOK rows carry no cost, so this is their only usage figure. */
+ tokens: z.number().int().optional(),
+})
+export type OrganizationUsageBreakdownRow = z.output
+
+export const organizationUsageBreakdownResponseSchema = z.object({
+ dimension: usageBreakdownDimensionSchema,
+ rows: z.array(organizationUsageBreakdownRowSchema),
+ /** The truncated tail, so the visible rows plus this reconcile to `totalCredits`. */
+ other: z.object({
+ credits: z.number(),
+ events: z.number().int(),
+ rowCount: z.number().int(),
+ /** Tokens for the omitted rows, so the token-denominated BYOK tab still adds up. */
+ tokens: z.number().int().nonnegative(),
+ }),
+ totalCredits: z.number(),
+})
+export type OrganizationUsageBreakdown = z.output
+
+export const organizationUsageEventSchema = z.object({
+ id: z.string(),
+ createdAt: z.string(),
+ source: z.string(),
+ description: z.string(),
+ workflowName: z.string().nullable(),
+ credits: z.number(),
+ hasCost: z.boolean(),
+})
+export type OrganizationUsageEvent = z.output
+
+export const organizationUsageEventsResponseSchema = z.object({
+ events: z.array(organizationUsageEventSchema),
+ nextCursor: z.string().optional(),
+ hasMore: z.boolean(),
+})
+export type OrganizationUsageEventPage = z.output
+
+export const getOrganizationUsageSummaryContract = defineRouteContract({
+ method: 'GET',
+ path: '/api/organizations/[id]/usage/summary',
+ params: z.object({ id: organizationIdSchema }),
+ query: organizationUsageSummaryQuerySchema,
+ response: { mode: 'json', schema: organizationUsageSummaryResponseSchema },
+})
+
+export const getOrganizationUsageBreakdownContract = defineRouteContract({
+ method: 'GET',
+ path: '/api/organizations/[id]/usage/breakdown',
+ params: z.object({ id: organizationIdSchema }),
+ query: organizationUsageBreakdownQuerySchema,
+ response: { mode: 'json', schema: organizationUsageBreakdownResponseSchema },
+})
+
+export const listOrganizationUsageEventsContract = defineRouteContract({
+ method: 'GET',
+ path: '/api/organizations/[id]/usage/events',
+ params: z.object({ id: organizationIdSchema }),
+ query: organizationUsageEventsQuerySchema,
+ response: { mode: 'json', schema: organizationUsageEventsResponseSchema },
+})
+
+/** `mode: 'text'` — a CSV body has no JSON schema to validate. */
+export const exportOrganizationUsageContract = defineRouteContract({
+ method: 'GET',
+ path: '/api/organizations/[id]/usage/export',
+ params: z.object({ id: organizationIdSchema }),
+ query: organizationUsageExportQuerySchema,
+ response: { mode: 'text' },
+})
diff --git a/apps/sim/lib/api/contracts/v1/admin/organizations.ts b/apps/sim/lib/api/contracts/v1/admin/organizations.ts
index 463b92fb1c4..418bf7ccf14 100644
--- a/apps/sim/lib/api/contracts/v1/admin/organizations.ts
+++ b/apps/sim/lib/api/contracts/v1/admin/organizations.ts
@@ -209,6 +209,24 @@ export const adminV1ListOrganizationsContract = defineRouteContract({
},
})
+/**
+ * Creates the organization and its owner membership, and deliberately nothing else.
+ *
+ * Organization settings are reached through a workspace the organization owns, so a
+ * brand-new organization with none is not yet administrable. Closing that inside
+ * this call was tried and removed: attaching the owner's existing workspaces cannot
+ * join the creation transaction (it runs its own, under a lock order that exists to
+ * avoid deadlocking against invitation acceptance), so it could only ever be
+ * best-effort — leaving a committed organization, a response that could not honestly
+ * report the outcome, and a retry blocked by the existing-membership check.
+ *
+ * This codebase already solves it properly elsewhere. `AdminMemberOperationView`
+ * tracks workspace moves with `pending | processing | dead_letter | applied` and
+ * per-workspace retry, and the enterprise-owner-claim path creates the workspace and
+ * the organization in one transaction and enqueues an outbox event for the rest.
+ * Provisioning a workspace for an organization belongs on one of those paths, not
+ * inline here.
+ */
export const adminV1CreateOrganizationContract = defineRouteContract({
method: 'POST',
path: '/api/v1/admin/organizations',
diff --git a/apps/sim/lib/billing/application/organization-usage/authorized-organization-usage-use-case.ts b/apps/sim/lib/billing/application/organization-usage/authorized-organization-usage-use-case.ts
new file mode 100644
index 00000000000..edef9a963ec
--- /dev/null
+++ b/apps/sim/lib/billing/application/organization-usage/authorized-organization-usage-use-case.ts
@@ -0,0 +1,107 @@
+import type { Principal } from '@sim/auth/principal'
+import type {
+ OrganizationUsageOperation,
+ OrganizationUsagePrincipal,
+} from '@/lib/billing/application/organization-usage/operations'
+import { getOrganizationSubscription } from '@/lib/billing/core/billing'
+import {
+ type ResolvedUsagePeriod,
+ resolveSubscriptionUsagePeriodOrDefault,
+} from '@/lib/billing/core/reporting-period'
+import { isOrganizationFeatureEntitled } from '@/lib/billing/core/subscription'
+import type { BillingEntity } from '@/lib/billing/core/usage-log'
+import { canUserManageBillingEntity } from '@/lib/billing/core/workspace-billing-authority'
+import { ForbiddenOperationError, type OperationUseCase } from '@/lib/core/application'
+import { isUsageMonitoringEnabled } from '@/lib/core/config/env-flags'
+
+export interface AuthorizedOrganizationUsageContext {
+ organizationId: string
+ billingEntity: BillingEntity
+ actorUserId: string
+ /**
+ * Resolved once per request and shared by every query in the use case. Re-resolving
+ * per query is how the tiles, the chart, and the event log would come to describe
+ * three slightly different windows.
+ */
+ period: ResolvedUsagePeriod
+}
+
+interface AuthorizedOrganizationUsageDefinition {
+ operation: O
+ organizationId(input: I): string
+ execute(args: {
+ principal: OrganizationUsagePrincipal
+ input: I
+ context: AuthorizedOrganizationUsageContext
+ }): Promise
+}
+
+function requireOrganizationUsagePrincipal(
+ principal: Principal,
+ operation: OrganizationUsageOperation
+): asserts principal is OrganizationUsagePrincipal {
+ if (!operation.principalKinds.some((kind) => kind === principal.kind)) {
+ throw new ForbiddenOperationError(
+ 'PRINCIPAL_KIND_NOT_PERMITTED',
+ `Principal kind ${principal.kind} cannot perform operation ${operation.id}`
+ )
+ }
+}
+
+/**
+ * Gate order for every organization usage read. Each step is a distinct refusal so a
+ * failure says which rule stopped it.
+ *
+ * 1. Principal kind — session only.
+ * 2. Billing authority — organization admin or owner. A workspace `admin` is
+ * explicitly not sufficient; this is pooled spend across every member.
+ * 3. Entitlement — enterprise plan on hosted, `USAGE_MONITORING_ENABLED` on
+ * self-hosted. Reuses audit-logs' error code so the client handles an
+ * entitlement refusal identically across EE settings.
+ */
+export function defineAuthorizedOrganizationUsageUseCase<
+ const O extends OrganizationUsageOperation,
+ I,
+ R,
+>(definition: AuthorizedOrganizationUsageDefinition): OperationUseCase {
+ return {
+ operation: definition.operation,
+ async execute({ principal, input }) {
+ requireOrganizationUsagePrincipal(principal, definition.operation)
+ const actorUserId = principal.userId
+ const organizationId = definition.organizationId(input)
+ const billingEntity: BillingEntity = { type: 'organization', id: organizationId }
+
+ if (!(await canUserManageBillingEntity(billingEntity, actorUserId))) {
+ throw new ForbiddenOperationError(
+ 'ORGANIZATION_ADMIN_REQUIRED',
+ 'Organization admin or owner authority is required to read pooled usage'
+ )
+ }
+
+ /**
+ * One call covers both the plan and the deployment: with billing on it checks
+ * the enterprise plan; with billing off — a self-hosted deployment, where there
+ * is no plan to consult — it answers `USAGE_MONITORING_ENABLED`. That is the
+ * same flag the navigation gate reads, so a section can never be visible here
+ * and rejected there. Calling `isOrganizationOnEnterprisePlan` directly would
+ * answer `true` for every self-hosted organization.
+ */
+ if (!(await isOrganizationFeatureEntitled(organizationId, isUsageMonitoringEnabled))) {
+ throw new ForbiddenOperationError(
+ 'ENTERPRISE_PLAN_REQUIRED',
+ 'Active enterprise subscription required'
+ )
+ }
+
+ const subscription = await getOrganizationSubscription(organizationId)
+ const period = resolveSubscriptionUsagePeriodOrDefault(subscription ?? {})
+
+ return definition.execute({
+ principal,
+ input,
+ context: { organizationId, billingEntity, actorUserId, period },
+ })
+ },
+ }
+}
diff --git a/apps/sim/lib/billing/application/organization-usage/export-organization-usage-events.ts b/apps/sim/lib/billing/application/organization-usage/export-organization-usage-events.ts
new file mode 100644
index 00000000000..c5cd4a51ade
--- /dev/null
+++ b/apps/sim/lib/billing/application/organization-usage/export-organization-usage-events.ts
@@ -0,0 +1,117 @@
+import { defineAuthorizedOrganizationUsageUseCase } from '@/lib/billing/application/organization-usage/authorized-organization-usage-use-case'
+import { organizationUsageOperations } from '@/lib/billing/application/organization-usage/operations'
+import {
+ resolveUsageAnalyticsWindow,
+ type UsageWindowPreset,
+ usageWindowLedgerFilter,
+} from '@/lib/billing/core/usage-analytics'
+import { getBillingEntityUsageLogs } from '@/lib/billing/core/usage-log'
+import { CREDIT_MULTIPLIER } from '@/lib/billing/credits/conversion'
+import type { InternalUsageLogSource } from '@/lib/billing/usage-sources'
+
+/**
+ * Circuit breaker, not a UX boundary. An enterprise ledger is genuinely large, so
+ * hitting the cap truncates and says so rather than failing the download.
+ */
+export const USAGE_EXPORT_SAFETY_CAP = 100_000
+const EXPORT_PAGE_SIZE = 1000
+
+export interface OrganizationUsageExportInput {
+ organizationId: string
+ preset: UsageWindowPreset
+ startDate?: Date
+ endDate?: Date
+ /** Viewer calendar, so a date-only custom bound means midnight there. */
+ timezone?: string
+ source?: InternalUsageLogSource[]
+}
+
+export interface OrganizationUsageExportRow {
+ createdAt: string
+ source: string
+ description: string
+ workflowName: string | null
+ /**
+ * Unrounded, unlike the event list's integer credits.
+ *
+ * A CSV is an analysis surface, and rounding each row to a whole credit printed a
+ * real sub-credit charge as `0` — the charge disappearing rather than reading as
+ * small. Full precision here also makes the column summable, which the previous
+ * `"N credits"` label never was.
+ */
+ credits: number
+}
+
+export interface OrganizationUsageExportResult {
+ rows: OrganizationUsageExportRow[]
+ truncated: boolean
+}
+
+/**
+ * Every event in the window, paged to the cap.
+ *
+ * Returns data, not CSV: serialization is presentation and belongs in the route, so
+ * this module stays free of any HTTP concern.
+ */
+export const exportOrganizationUsageEvents = defineAuthorizedOrganizationUsageUseCase({
+ operation: organizationUsageOperations.exportEvents,
+ organizationId: (input: OrganizationUsageExportInput) => input.organizationId,
+ async execute({ input, context }): Promise {
+ const window = resolveUsageAnalyticsWindow({
+ preset: input.preset,
+ period: context.period,
+ customStart: input.startDate,
+ customEnd: input.endDate,
+ timezone: input.timezone,
+ })
+ const ledgerFilter = usageWindowLedgerFilter(window)
+
+ const rows: OrganizationUsageExportRow[] = []
+ let cursor: string | undefined
+ /**
+ * The cursor row's timestamp, carried forward from the page that produced it.
+ *
+ * Without it `getUsageLogs` resolves the cursor with a lookup on the primary
+ * before it can read the replica — once per page, up to a hundred times for a
+ * capped export. This loop is the exact case that option was added for.
+ */
+ let cursorCreatedAt: Date | undefined
+ let truncated = false
+
+ while (rows.length < USAGE_EXPORT_SAFETY_CAP) {
+ const page = await getBillingEntityUsageLogs(context.billingEntity, {
+ // One derivation for both predicates, so the CSV covers exactly the rows the
+ // summary and breakdowns aggregate over.
+ ...ledgerFilter,
+ ...(input.source?.length ? { source: input.source } : {}),
+ limit: EXPORT_PAGE_SIZE,
+ ...(cursor ? { cursor } : {}),
+ ...(cursorCreatedAt ? { cursorCreatedAt } : {}),
+ // Each page would otherwise repeat the same cursor-independent aggregate
+ // for a total this export never reads.
+ includeSummary: false,
+ })
+
+ for (const log of page.logs) {
+ rows.push({
+ createdAt: log.createdAt,
+ source: log.source,
+ description: log.description,
+ workflowName: log.workflowName ?? null,
+ credits: log.cost * CREDIT_MULTIPLIER,
+ })
+ }
+
+ if (!page.pagination.hasMore || !page.pagination.nextCursor) break
+ const cursorRow = page.logs.find((log) => log.id === page.pagination.nextCursor)
+ cursor = page.pagination.nextCursor
+ cursorCreatedAt = cursorRow ? new Date(cursorRow.createdAt) : undefined
+ if (rows.length >= USAGE_EXPORT_SAFETY_CAP) {
+ truncated = true
+ break
+ }
+ }
+
+ return { rows: rows.slice(0, USAGE_EXPORT_SAFETY_CAP), truncated }
+ },
+})
diff --git a/apps/sim/lib/billing/application/organization-usage/get-organization-usage-breakdown.ts b/apps/sim/lib/billing/application/organization-usage/get-organization-usage-breakdown.ts
new file mode 100644
index 00000000000..1e760bc58ac
--- /dev/null
+++ b/apps/sim/lib/billing/application/organization-usage/get-organization-usage-breakdown.ts
@@ -0,0 +1,215 @@
+import { defineAuthorizedOrganizationUsageUseCase } from '@/lib/billing/application/organization-usage/authorized-organization-usage-use-case'
+import { organizationUsageOperations } from '@/lib/billing/application/organization-usage/operations'
+import {
+ buildUsageAnalyticsScope,
+ foldUsageBreakdown,
+ type MergeableRow,
+ mergeRowsByKey,
+ resolveUsageAnalyticsWindow,
+ USAGE_NULL_KEY_LABELS,
+ type UsageBreakdownDimension,
+ type UsageWindowPreset,
+} from '@/lib/billing/core/usage-analytics'
+import {
+ readUsageBreakdown,
+ readUsageEntityNames,
+} from '@/lib/billing/core/usage-analytics-queries'
+import { apportionCredits, dollarsToCredits } from '@/lib/billing/credits/conversion'
+import {
+ BILLING_USAGE_LOG_SOURCE_LABELS,
+ type InternalUsageLogSource,
+ toBillingUsageLogSource,
+} from '@/lib/billing/usage-sources'
+import { getProviderFromModel } from '@/providers/models'
+
+export interface OrganizationUsageBreakdownInput {
+ organizationId: string
+ dimension: UsageBreakdownDimension
+ preset: UsageWindowPreset
+ startDate?: Date
+ endDate?: Date
+ /** Viewer calendar, so a date-only custom bound means midnight there. */
+ timezone?: string
+ /** Narrows to one workspace, for the Workspaces drill-down. */
+ workspaceId?: string
+ limit: number
+}
+
+export interface OrganizationUsageBreakdownRow {
+ id: string
+ label: string
+ credits: number
+ events: number
+ share: number
+ providerId?: string
+ tokens?: number
+}
+
+export interface OrganizationUsageBreakdownResult {
+ dimension: UsageBreakdownDimension
+ rows: OrganizationUsageBreakdownRow[]
+ other: { credits: number; events: number; rowCount: number; tokens: number }
+ totalCredits: number
+}
+
+/** Entity-backed dimensions need a second lookup to turn ids into names. */
+const NAMED_DIMENSIONS = new Set(['member', 'workspace', 'workflow'])
+
+/** Provider ids that read better with their conventional casing. */
+const PROVIDER_LABELS: Readonly> = {
+ openai: 'OpenAI',
+ anthropic: 'Anthropic',
+ google: 'Google',
+ 'azure-openai': 'Azure OpenAI',
+ deepseek: 'DeepSeek',
+ xai: 'xAI',
+ groq: 'Groq',
+ cerebras: 'Cerebras',
+ ollama: 'Ollama',
+ openrouter: 'OpenRouter',
+ mistral: 'Mistral',
+}
+
+function providerLabel(providerId: string): string {
+ return PROVIDER_LABELS[providerId] ?? providerId
+}
+
+export const getOrganizationUsageBreakdown = defineAuthorizedOrganizationUsageUseCase({
+ operation: organizationUsageOperations.readBreakdown,
+ organizationId: (input: OrganizationUsageBreakdownInput) => input.organizationId,
+ async execute({ input, context }): Promise {
+ const window = resolveUsageAnalyticsWindow({
+ preset: input.preset,
+ period: context.period,
+ customStart: input.startDate,
+ customEnd: input.endDate,
+ timezone: input.timezone,
+ })
+ const scope = buildUsageAnalyticsScope(context.billingEntity, window)
+ const raw = await readUsageBreakdown(scope, input.dimension, input.workspaceId)
+
+ /**
+ * Re-key onto what the panel actually displays before ranking.
+ *
+ * Two dimensions are coarser than their SQL grouping column: `source` shows one
+ * "Sim Chat" row for the ledger's `copilot` *and* `workspace-chat`, and `byok`
+ * shows one row per provider rather than per model. Ranking the raw rows would
+ * render the same label twice with the total split between them.
+ */
+ const rows: MergeableRow[] =
+ input.dimension === 'source'
+ ? mergeRowsByKey(raw, (key) =>
+ key ? toBillingUsageLogSource(key as InternalUsageLogSource) : null
+ )
+ : input.dimension === 'byok'
+ ? mergeRowsByKey(raw, (key) => (key ? getProviderFromModel(key) : null))
+ : raw
+
+ const totalCost = rows.reduce((sum, row) => sum + (Number(row.cost) || 0), 0)
+
+ /**
+ * Names are hydrated for the surviving keys only — joining inside the aggregate
+ * would break the index-only scan the member dimension depends on.
+ *
+ * Sorted before slicing: the breakdown query only groups, so Postgres returns its
+ * aggregate in arbitrary order. Slicing that directly hydrated an arbitrary subset
+ * while the fold below ranks by cost, so a top row whose name was never fetched
+ * fell through to `?? key` and rendered a raw id. The margin over `limit` covers
+ * the fold's label tiebreak pulling in a row just past the cut.
+ */
+ const rankedIds = [...rows]
+ .sort((left, right) => Number(right.cost) - Number(left.cost))
+ .slice(0, input.limit * 2)
+ .map((row) => row.key)
+ .filter((key): key is string => Boolean(key))
+ const names = NAMED_DIMENSIONS.has(input.dimension)
+ ? await readUsageEntityNames(input.dimension, rankedIds)
+ : new Map()
+
+ const labelFor = (key: string | null): string => {
+ /**
+ * A null key means something different per dimension, and one shared
+ * "Unattributed" label got both wrong: on Workspaces it is usage owned by no
+ * workspace, on Workflows it is usage that never came from a workflow — which
+ * is most of an organization's spend, and reading it as an attribution failure
+ * is what made that list useless.
+ */
+ if (!key) return USAGE_NULL_KEY_LABELS[input.dimension]
+ if (input.dimension === 'source') {
+ return BILLING_USAGE_LOG_SOURCE_LABELS[key as keyof typeof BILLING_USAGE_LOG_SOURCE_LABELS]
+ }
+ if (input.dimension === 'byok') return providerLabel(key)
+ if (input.dimension === 'model') return key
+ // A deleted workspace or workflow nulls its id on the ledger row, so a key that
+ // resolves to no name is a live entity we could not read — not a deleted one.
+ return names.get(key) ?? key
+ }
+
+ // BYOK is denominated in tokens and every row costs zero, so ranking it by cost
+ // would order the list alphabetically and call the result "top providers".
+ const fold = foldUsageBreakdown(
+ rows,
+ totalCost,
+ labelFor,
+ input.limit,
+ input.dimension === 'byok' ? 'tokens' : 'cost'
+ )
+ const tokensByKey = new Map(
+ rows.map((row) => [row.key ?? '', (row.inputTokens ?? 0) + (row.outputTokens ?? 0)])
+ )
+ const isModelDimension = input.dimension === 'model' || input.dimension === 'byok'
+
+ /**
+ * One apportionment across the visible rows and the remainder together.
+ *
+ * Converting each row independently rounds each one, so with sub-credit
+ * fractions the rows and `Other` no longer add up — which defeats the entire
+ * reason `Other` is rendered. Largest-remainder over the whole set is what
+ * `apportionCredits` is for, and it is the same routine the per-log credit
+ * costs use, so a row cannot read differently here than in the event list.
+ *
+ * Keyed positionally: a row id may be `''` for a null grouping key, and any
+ * id could otherwise collide with the remainder's own key.
+ */
+ const apportioned = apportionCredits([
+ ...fold.rows.map((row, index) => ({ key: `row:${index}` as const, dollars: row.cost })),
+ { key: 'other' as const, dollars: fold.other.cost },
+ ])
+
+ return {
+ dimension: input.dimension,
+ rows: fold.rows.map((row, index) => {
+ const tokens = tokensByKey.get(row.id) ?? 0
+ return {
+ id: row.id,
+ label: row.label,
+ credits: apportioned[`row:${index}`] ?? 0,
+ events: row.events,
+ share: row.share,
+ ...(isModelDimension && tokens > 0 ? { tokens } : {}),
+ ...(input.dimension === 'byok' ? { providerId: row.id } : {}),
+ ...(input.dimension === 'model' && row.id
+ ? { providerId: getProviderFromModel(row.id) }
+ : {}),
+ }
+ }),
+ other: {
+ credits: apportioned.other ?? 0,
+ events: fold.other.events,
+ rowCount: fold.other.rowCount,
+ /**
+ * The omitted rows' tokens, so the BYOK tab still accounts for providers
+ * past the visible limit instead of hiding them behind an em dash.
+ */
+ tokens: fold.other.tokens,
+ },
+ /**
+ * The scope total, which the rows need not sum to: the workflow dimension is
+ * explicitly the workflow-attributed subset of it. Where they do sum to it —
+ * every other dimension — the apportionment above lands on this exact figure,
+ * because it derives its target from the same dollar sum.
+ */
+ totalCredits: dollarsToCredits(fold.totalCost),
+ }
+ },
+})
diff --git a/apps/sim/lib/billing/application/organization-usage/get-organization-usage-summary.ts b/apps/sim/lib/billing/application/organization-usage/get-organization-usage-summary.ts
new file mode 100644
index 00000000000..48616c6f1a9
--- /dev/null
+++ b/apps/sim/lib/billing/application/organization-usage/get-organization-usage-summary.ts
@@ -0,0 +1,113 @@
+import { defineAuthorizedOrganizationUsageUseCase } from '@/lib/billing/application/organization-usage/authorized-organization-usage-use-case'
+import { organizationUsageOperations } from '@/lib/billing/application/organization-usage/operations'
+import type { UsagePeriodSource } from '@/lib/billing/core/reporting-period'
+import {
+ buildUsageAnalyticsScope,
+ densifyUsageSeries,
+ resolvePreviousPeriod,
+ resolveUsageAnalyticsWindow,
+ resolveUsageBucket,
+ type UsageBucket,
+ type UsageWindowPreset,
+ usageWindowBounds,
+} from '@/lib/billing/core/usage-analytics'
+import { readUsageTimeSeries, readUsageTotals } from '@/lib/billing/core/usage-analytics-queries'
+import { apportionCredits, dollarsToCredits } from '@/lib/billing/credits/conversion'
+
+export interface OrganizationUsageSummaryInput {
+ organizationId: string
+ preset: UsageWindowPreset
+ startDate?: Date
+ endDate?: Date
+ timezone: string
+}
+
+export interface OrganizationUsageSummaryResult {
+ window: { start: string; end: string; source: UsagePeriodSource | 'range' }
+ bucket: UsageBucket
+ totals: { credits: number }
+ previousTotals: { credits: number } | null
+ series: Array<{ timestamp: string; credits: number; events: number }>
+}
+
+/**
+ * Everything above the fold in one round trip: headline totals, the delta, and the
+ * trend series. Every read here is index-covered, so this is cheap enough to be what
+ * first paint waits on — the breakdowns, which are not, are fetched separately.
+ */
+export const getOrganizationUsageSummary = defineAuthorizedOrganizationUsageUseCase({
+ operation: organizationUsageOperations.readSummary,
+ organizationId: (input: OrganizationUsageSummaryInput) => input.organizationId,
+ async execute({ input, context }): Promise {
+ const window = resolveUsageAnalyticsWindow({
+ preset: input.preset,
+ period: context.period,
+ customStart: input.startDate,
+ customEnd: input.endDate,
+ timezone: input.timezone,
+ })
+ const bucket = resolveUsageBucket(window)
+ const scope = buildUsageAnalyticsScope(context.billingEntity, window)
+
+ /**
+ * The comparison window only exists when it is exactly derivable.
+ *
+ * `resolvePreviousPeriod` directly, not the `previous-period` preset: that preset
+ * must always return *something*, because a user who explicitly asks for the
+ * previous period has to see a window — so for a stripe period it approximates one
+ * by stepping back the current period's length. That approximation is fine as a
+ * destination and wrong as a baseline, since Stripe periods are not equal-length
+ * and the delta would silently compare against a window that is not the previous
+ * period. No delta beats a delta measured against the wrong window.
+ */
+ const previousPeriod =
+ input.preset === 'current-period' ? resolvePreviousPeriod(context.period) : null
+ const comparison = previousPeriod ? { kind: 'period' as const, period: previousPeriod } : null
+
+ const [totals, seriesRows, previous] = await Promise.all([
+ readUsageTotals(scope),
+ readUsageTimeSeries(scope, bucket, input.timezone),
+ comparison
+ ? readUsageTotals(buildUsageAnalyticsScope(context.billingEntity, comparison))
+ : Promise.resolve(null),
+ ])
+
+ const bounds = usageWindowBounds(window)
+ /**
+ * One apportionment across the buckets, not a per-bucket round.
+ *
+ * A day costing less than half a credit rounds to zero on its own, so an
+ * organization spending a fraction of a credit a day drew a flat empty chart under
+ * a positive headline — the reader's conclusion being that the chart is broken.
+ * Largest-remainder distributes the period's rounded total across its buckets, so
+ * the bars sum to the headline and a nonzero day is never drawn as zero.
+ *
+ * Same routine, and the same reasoning, as the breakdown's rows-plus-remainder.
+ */
+ const densified = densifyUsageSeries(seriesRows, window, bucket, input.timezone)
+ const apportioned = apportionCredits(
+ densified.map((point, index) => ({ key: `b:${index}` as const, dollars: point.cost }))
+ )
+ const bucketCredits = densified.map((point, index) => ({
+ timestamp: point.timestamp,
+ credits: apportioned[`b:${index}`] ?? 0,
+ events: point.events,
+ }))
+
+ return {
+ window: {
+ start: bounds.start.toISOString(),
+ end: bounds.end.toISOString(),
+ source: window.kind === 'range' ? 'range' : window.period.source,
+ },
+ bucket,
+ totals: { credits: dollarsToCredits(totals.cost) },
+ previousTotals: previous ? { credits: dollarsToCredits(previous.cost) } : null,
+ series: bucketCredits.map((point) => ({
+ timestamp: point.timestamp,
+ credits: point.credits,
+ events: point.events,
+ })),
+ }
+ },
+})
diff --git a/apps/sim/lib/billing/application/organization-usage/list-organization-usage-events.ts b/apps/sim/lib/billing/application/organization-usage/list-organization-usage-events.ts
new file mode 100644
index 00000000000..9a1e2b80607
--- /dev/null
+++ b/apps/sim/lib/billing/application/organization-usage/list-organization-usage-events.ts
@@ -0,0 +1,84 @@
+import { defineAuthorizedOrganizationUsageUseCase } from '@/lib/billing/application/organization-usage/authorized-organization-usage-use-case'
+import { organizationUsageOperations } from '@/lib/billing/application/organization-usage/operations'
+import {
+ resolveUsageAnalyticsWindow,
+ type UsageWindowPreset,
+ usageWindowLedgerFilter,
+} from '@/lib/billing/core/usage-analytics'
+import { getBillingEntityUsageLogs } from '@/lib/billing/core/usage-log'
+import { dollarsToCredits } from '@/lib/billing/credits/conversion'
+import type { InternalUsageLogSource } from '@/lib/billing/usage-sources'
+
+export interface OrganizationUsageEventsInput {
+ organizationId: string
+ preset: UsageWindowPreset
+ startDate?: Date
+ endDate?: Date
+ /** Viewer calendar, so a date-only custom bound means midnight there. */
+ timezone?: string
+ source?: InternalUsageLogSource[]
+ limit: number
+ cursor?: string
+}
+
+export interface OrganizationUsageEvent {
+ id: string
+ createdAt: string
+ source: string
+ description: string
+ workflowName: string | null
+ credits: number
+ hasCost: boolean
+}
+
+export interface OrganizationUsageEventsResult {
+ events: OrganizationUsageEvent[]
+ nextCursor?: string
+ hasMore: boolean
+}
+
+/**
+ * One page of the organization's raw ledger.
+ *
+ * Reuses `getBillingEntityUsageLogs` rather than rebuilding pagination: keyset
+ * ordering, cursor resolution, and the workflow-name join are already correct there.
+ * `includeSummary` stays off because the page header reads its total from the summary
+ * endpoint — recomputing the full-filter aggregate on every scroll would pay for the
+ * same scan once per page.
+ */
+export const listOrganizationUsageEvents = defineAuthorizedOrganizationUsageUseCase({
+ operation: organizationUsageOperations.listEvents,
+ organizationId: (input: OrganizationUsageEventsInput) => input.organizationId,
+ async execute({ input, context }): Promise {
+ const window = resolveUsageAnalyticsWindow({
+ preset: input.preset,
+ period: context.period,
+ customStart: input.startDate,
+ customEnd: input.endDate,
+ timezone: input.timezone,
+ })
+ const result = await getBillingEntityUsageLogs(context.billingEntity, {
+ // One derivation for both predicates, so this list covers exactly the rows the
+ // summary and breakdowns aggregate over.
+ ...usageWindowLedgerFilter(window),
+ ...(input.source?.length ? { source: input.source } : {}),
+ limit: input.limit,
+ ...(input.cursor ? { cursor: input.cursor } : {}),
+ includeSummary: false,
+ })
+
+ return {
+ events: result.logs.map((log) => ({
+ id: log.id,
+ createdAt: log.createdAt,
+ source: log.source,
+ description: log.description,
+ workflowName: log.workflowName ?? null,
+ credits: dollarsToCredits(log.cost),
+ hasCost: log.cost > 0,
+ })),
+ ...(result.pagination.nextCursor ? { nextCursor: result.pagination.nextCursor } : {}),
+ hasMore: result.pagination.hasMore,
+ }
+ },
+})
diff --git a/apps/sim/lib/billing/application/organization-usage/operations.ts b/apps/sim/lib/billing/application/organization-usage/operations.ts
new file mode 100644
index 00000000000..a98aa7d9c6f
--- /dev/null
+++ b/apps/sim/lib/billing/application/organization-usage/operations.ts
@@ -0,0 +1,53 @@
+import type { Principal } from '@sim/auth/principal'
+import type { ApplicationOperation } from '@/lib/core/application'
+
+/**
+ * Session only.
+ *
+ * An organization's pooled ledger discloses every member's model spend, which is why
+ * `workspace-billing-authority` treats organization membership alone as insufficient
+ * for it. There is no API-key consumer of this surface today, and adding one should
+ * be a deliberate decision rather than something inherited from a default.
+ */
+export type OrganizationUsagePrincipal = Extract
+
+export interface OrganizationUsageOperation
+ extends ApplicationOperation {
+ readonly authority: 'organization_billing_admin'
+ readonly organizationRoles: readonly ['admin', 'owner']
+ readonly workspaceApiKey: 'deny'
+ readonly principalKinds: readonly ['session']
+}
+
+function defineOrganizationUsageOperation(
+ operation: OrganizationUsageOperation
+): OrganizationUsageOperation {
+ if ((operation.principalKinds as readonly string[]).some((kind) => kind !== 'session')) {
+ throw new Error(
+ `Organization usage operation ${operation.id} may only be performed by a session`
+ )
+ }
+ Object.freeze(operation.organizationRoles)
+ Object.freeze(operation.principalKinds)
+ return Object.freeze(operation)
+}
+
+const BASE = {
+ authority: 'organization_billing_admin',
+ organizationRoles: ['admin', 'owner'],
+ workspaceApiKey: 'deny',
+ principalKinds: ['session'],
+} as const satisfies Omit
+
+export const organizationUsageOperations = {
+ readSummary: defineOrganizationUsageOperation({ id: 'organization_usage.summary.read', ...BASE }),
+ readBreakdown: defineOrganizationUsageOperation({
+ id: 'organization_usage.breakdown.read',
+ ...BASE,
+ }),
+ listEvents: defineOrganizationUsageOperation({ id: 'organization_usage.events.list', ...BASE }),
+ exportEvents: defineOrganizationUsageOperation({
+ id: 'organization_usage.events.export',
+ ...BASE,
+ }),
+} as const
diff --git a/apps/sim/lib/billing/application/organization-usage/organization-usage-use-cases.test.ts b/apps/sim/lib/billing/application/organization-usage/organization-usage-use-cases.test.ts
new file mode 100644
index 00000000000..173e15826b5
--- /dev/null
+++ b/apps/sim/lib/billing/application/organization-usage/organization-usage-use-cases.test.ts
@@ -0,0 +1,128 @@
+/**
+ * @vitest-environment node
+ */
+import type { PersonalApiKeyPrincipal, SessionPrincipal } from '@sim/auth/principal'
+import { setEnvFlags } from '@sim/testing'
+import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
+
+const mocks = vi.hoisted(() => ({
+ canUserManageBillingEntity: vi.fn(),
+ isOrganizationFeatureEntitled: vi.fn(),
+ getOrganizationSubscription: vi.fn(),
+ readUsageTotals: vi.fn(),
+ readUsageTimeSeries: vi.fn(),
+}))
+
+vi.mock('@/lib/billing/core/workspace-billing-authority', () => ({
+ canUserManageBillingEntity: mocks.canUserManageBillingEntity,
+}))
+vi.mock('@/lib/billing/core/subscription', () => ({
+ isOrganizationFeatureEntitled: mocks.isOrganizationFeatureEntitled,
+}))
+vi.mock('@/lib/billing/core/billing', () => ({
+ getOrganizationSubscription: mocks.getOrganizationSubscription,
+}))
+vi.mock('@/lib/billing/core/usage-analytics-queries', () => ({
+ readUsageTotals: mocks.readUsageTotals,
+ readUsageTimeSeries: mocks.readUsageTimeSeries,
+ readUsageBreakdown: vi.fn(),
+ readUsageEntityNames: vi.fn(),
+}))
+
+import { getOrganizationUsageSummary } from '@/lib/billing/application/organization-usage/get-organization-usage-summary'
+import { ForbiddenOperationError } from '@/lib/core/application'
+
+const ORG = 'org-1'
+const session: SessionPrincipal = { kind: 'session', userId: 'admin-1', sessionId: 'session-1' }
+const personalKey: PersonalApiKeyPrincipal = {
+ kind: 'personal_api_key',
+ userId: 'admin-1',
+ keyId: 'key-1',
+}
+
+const input = {
+ organizationId: ORG,
+ preset: 'current-period' as const,
+ timezone: 'UTC',
+}
+
+function run(principal: SessionPrincipal | PersonalApiKeyPrincipal = session) {
+ return getOrganizationUsageSummary.execute({ principal, input })
+}
+
+async function codeOf(promise: Promise): Promise {
+ try {
+ await promise
+ throw new Error('expected the use case to refuse')
+ } catch (error) {
+ expect(error).toBeInstanceOf(ForbiddenOperationError)
+ return (error as ForbiddenOperationError).detailCode
+ }
+}
+
+describe('organization usage authorization', () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ setEnvFlags({ isBillingEnabled: true, isHosted: true })
+ mocks.canUserManageBillingEntity.mockResolvedValue(true)
+ mocks.isOrganizationFeatureEntitled.mockResolvedValue(true)
+ mocks.getOrganizationSubscription.mockResolvedValue({
+ plan: 'enterprise',
+ periodStart: new Date('2026-08-01T00:00:00.000Z'),
+ periodEnd: new Date('2026-09-01T00:00:00.000Z'),
+ })
+ mocks.readUsageTotals.mockResolvedValue({ cost: 1 })
+ mocks.readUsageTimeSeries.mockResolvedValue([])
+ })
+
+ afterAll(() => {
+ setEnvFlags({ isBillingEnabled: false, isHosted: false })
+ })
+
+ it('refuses an API key: pooled usage discloses every member’s spend', async () => {
+ // The operation names `session` alone. Widening it is a deliberate decision, not
+ // something that should fall out of a principal shape happening to carry a userId.
+ expect(await codeOf(run(personalKey))).toBe('PRINCIPAL_KIND_NOT_PERMITTED')
+ expect(mocks.canUserManageBillingEntity).not.toHaveBeenCalled()
+ })
+
+ it('refuses a member who is not an organization admin', async () => {
+ mocks.canUserManageBillingEntity.mockResolvedValue(false)
+
+ expect(await codeOf(run())).toBe('ORGANIZATION_ADMIN_REQUIRED')
+ })
+
+ it('refuses an organization without the entitlement', async () => {
+ mocks.isOrganizationFeatureEntitled.mockResolvedValue(false)
+
+ expect(await codeOf(run())).toBe('ENTERPRISE_PLAN_REQUIRED')
+ })
+
+ it('checks authority before entitlement, so a non-admin learns nothing about the plan', async () => {
+ mocks.canUserManageBillingEntity.mockResolvedValue(false)
+ mocks.isOrganizationFeatureEntitled.mockResolvedValue(false)
+
+ expect(await codeOf(run())).toBe('ORGANIZATION_ADMIN_REQUIRED')
+ expect(mocks.isOrganizationFeatureEntitled).not.toHaveBeenCalled()
+ })
+
+ it('asks the entitlement helper, not the plan directly, so self-hosted stays gated', async () => {
+ // `isOrganizationOnEnterprisePlan` answers true for every organization once billing
+ // is off, which would hand the feature to every self-hosted deployment.
+ await run()
+
+ expect(mocks.isOrganizationFeatureEntitled).toHaveBeenCalledWith(ORG, expect.any(Boolean))
+ })
+
+ it('scopes the read to the caller’s organization and resolves its period once', async () => {
+ await run()
+
+ expect(mocks.getOrganizationSubscription).toHaveBeenCalledTimes(1)
+ // Both reads share one scope built from one resolved period, which is what keeps the
+ // totals and the chart describing the same rows.
+ const [totalsScope] = mocks.readUsageTotals.mock.calls[0]
+ const [seriesScope] = mocks.readUsageTimeSeries.mock.calls[0]
+ expect(JSON.stringify(totalsScope)).toContain(ORG)
+ expect(JSON.stringify(seriesScope)).toBe(JSON.stringify(totalsScope))
+ })
+})
diff --git a/apps/sim/lib/billing/core/usage-analytics-queries.ts b/apps/sim/lib/billing/core/usage-analytics-queries.ts
new file mode 100644
index 00000000000..fec2b8e0480
--- /dev/null
+++ b/apps/sim/lib/billing/core/usage-analytics-queries.ts
@@ -0,0 +1,230 @@
+import { dbReplica } from '@sim/db'
+import { usageLog, user, workflow, workspace } from '@sim/db/schema'
+import { and, eq, inArray, isNotNull, type SQL, sql } from 'drizzle-orm'
+import type { UsageBreakdownDimension, UsageBucket } from '@/lib/billing/core/usage-analytics'
+import { assertValidTimezone } from '@/lib/core/utils/timezone'
+import type { DbClient } from '@/lib/db/types'
+
+/**
+ * DB half of organization usage analytics. Every read runs on the replica and takes
+ * a prebuilt scope from `buildUsageAnalyticsScope`, so no query here decides period
+ * semantics for itself.
+ *
+ * Index note (`usage_log_billing_entity_created_at_cost_idx` covers
+ * `entityType, entityId, createdAt, userId, source, cost`): the series, the totals,
+ * and the member/source breakdowns are index-only. `workspace_id`, `workflow_id`,
+ * `description`, `metadata`, and `execution_id` are NOT in it, so the remaining
+ * reads heap-fetch per row — which is why they are issued only for the dimension
+ * actually being viewed.
+ */
+
+export interface UsageTimeSeriesRow {
+ bucketStart: string | null
+ cost: string
+ events: number
+}
+
+/** Index-only: reads `created_at` and `cost`, both covered. */
+export async function readUsageTimeSeries(
+ scope: SQL[],
+ bucket: UsageBucket,
+ timezone: string,
+ executor: DbClient = dbReplica
+): Promise {
+ assertValidTimezone(timezone)
+ const bucketStart = sql`to_char(
+ date_trunc(${bucket}, ${usageLog.createdAt} AT TIME ZONE ${timezone}),
+ 'YYYY-MM-DD"T"HH24:MI:SS'
+ )`
+
+ return (
+ executor
+ .select({
+ bucketStart: bucketStart.as('bucket_start'),
+ cost: sql`COALESCE(SUM(${usageLog.cost}), 0)`,
+ events: sql`COUNT(*)`.mapWith(Number),
+ })
+ .from(usageLog)
+ .where(and(...scope))
+ // Group by the output alias, not the expression. Re-rendering the fragment here
+ // emits a *textually different* one — the select list qualifies the column as
+ // `created_at`, the group-by as `usage_log.created_at` — and Postgres matches
+ // group-by expressions syntactically, so it rejects the query outright. It also
+ // duplicates the bound parameters.
+ .groupBy(sql`bucket_start`)
+ )
+}
+
+export interface UsageTotals {
+ cost: number
+}
+
+/**
+ * The headline figure, and the only one first paint waits on.
+ *
+ * Deliberately just `SUM(cost)`. A `COUNT(DISTINCT user_id)` alongside it forces a
+ * sort over every matching row — measured at 830ms of a 909ms query on production's
+ * largest organization (342k rows in a 30-day window), and the summary runs it twice
+ * because the delta compares two windows. Without it the same query is 79ms. Any
+ * per-actor figure added here must earn that cost by actually being displayed.
+ */
+export async function readUsageTotals(
+ scope: SQL[],
+ executor: DbClient = dbReplica
+): Promise {
+ const [row] = await executor
+ .select({
+ cost: sql`COALESCE(SUM(${usageLog.cost}), 0)`,
+ })
+ .from(usageLog)
+ .where(and(...scope))
+
+ return { cost: Number.parseFloat(row?.cost ?? '0') || 0 }
+}
+
+export interface UsageBreakdownRow {
+ key: string | null
+ cost: string
+ events: number
+ /** Model dimensions only — the ledger records tokens only for model categories. */
+ inputTokens?: number
+ outputTokens?: number
+}
+
+/** Dimensions keyed on `description`, which holds a model name for model categories. */
+const MODEL_DIMENSIONS = new Set(['model', 'byok'])
+
+function breakdownColumn(dimension: UsageBreakdownDimension) {
+ switch (dimension) {
+ case 'member':
+ return usageLog.userId
+ case 'workspace':
+ return usageLog.workspaceId
+ case 'workflow':
+ return usageLog.workflowId
+ case 'source':
+ return usageLog.source
+ case 'model':
+ case 'byok':
+ return usageLog.description
+ }
+}
+
+/**
+ * Ranked totals for one dimension.
+ *
+ * Aggregate-first: names are hydrated by {@link readUsageEntityNames} for the
+ * surviving keys only. Joining inside the aggregate would break index-only for
+ * `member` and force a nested loop across the whole window.
+ */
+export async function readUsageBreakdown(
+ scope: SQL[],
+ dimension: UsageBreakdownDimension,
+ /** Narrows to one workspace, for the Workspaces drill-down. */
+ workspaceId: string | undefined,
+ executor: DbClient = dbReplica
+): Promise {
+ const column = breakdownColumn(dimension)
+ const conditions = [...scope]
+ if (workspaceId) conditions.push(eq(usageLog.workspaceId, workspaceId))
+ /**
+ * `description` holds a model name only for the model categories; a tool or fixed
+ * row would otherwise appear as a phantom "model". The two model dimensions split
+ * on who paid: `model` is what Sim charged for, `byok` is the customer's own key.
+ */
+ if (dimension === 'model') conditions.push(eq(usageLog.category, 'model'))
+ if (dimension === 'byok') conditions.push(eq(usageLog.category, 'model_unbilled'))
+ /**
+ * Only `source = 'workflow'` rows ever carry a `workflow_id` — Chat, Agent block,
+ * Wand, knowledge base, and voice have none by construction, not by omission. So a
+ * workflow list excludes them outright; bucketing them into an "other" row put most
+ * of an organization's usage into a list it does not belong in.
+ */
+ if (dimension === 'workflow') conditions.push(isNotNull(usageLog.workflowId))
+
+ if (!MODEL_DIMENSIONS.has(dimension)) {
+ return (
+ executor
+ .select({
+ key: sql`${column}`,
+ cost: sql`COALESCE(SUM(${usageLog.cost}), 0)`,
+ events: sql`COUNT(*)`.mapWith(Number),
+ })
+ .from(usageLog)
+ .where(and(...conditions))
+ .groupBy(column)
+ /**
+ * Drops groups whose every row is reporting-only. Unbilled rows carry a user,
+ * a workspace, a workflow and `source = 'workflow'` like any other, so without
+ * this a BYOK-only member appeared in a credit-denominated list at 0 credits.
+ *
+ * As a `HAVING` on the aggregate rather than a `category` predicate on purpose:
+ * `category` is not in `usage_log_billing_entity_created_at_cost_idx`, so
+ * filtering on it would force a heap fetch on `member` and `source` — the two
+ * dimensions that are index-only today. `cost` is in that index, and only an
+ * unbilled row can sum to zero, since `recordUsage` admits nothing else at zero.
+ */
+ .having(sql`COALESCE(SUM(${usageLog.cost}), 0) > 0`)
+ )
+ }
+
+ // Already heap-reading `description`, so summing `metadata` costs nothing extra —
+ // and BYOK rows carry no cost at all, making tokens the only usage they can show.
+ return executor
+ .select({
+ key: sql`${column}`,
+ cost: sql`COALESCE(SUM(${usageLog.cost}), 0)`,
+ events: sql`COUNT(*)`.mapWith(Number),
+ inputTokens:
+ sql`COALESCE(SUM((${usageLog.metadata}->>'inputTokens')::bigint), 0)`.mapWith(
+ Number
+ ),
+ outputTokens:
+ sql`COALESCE(SUM((${usageLog.metadata}->>'outputTokens')::bigint), 0)`.mapWith(
+ Number
+ ),
+ })
+ .from(usageLog)
+ .where(and(...conditions))
+ .groupBy(column)
+}
+
+/**
+ * Display names for the top-N keys of an entity-backed dimension.
+ *
+ * Members fall back to email because a user may have no name set, and an empty row
+ * label is worse than an address.
+ */
+export async function readUsageEntityNames(
+ dimension: UsageBreakdownDimension,
+ ids: string[],
+ executor: DbClient = dbReplica
+): Promise> {
+ if (ids.length === 0) return new Map()
+
+ if (dimension === 'member') {
+ const rows = await executor
+ .select({ id: user.id, name: user.name, email: user.email })
+ .from(user)
+ .where(inArray(user.id, ids))
+ return new Map(rows.map((row) => [row.id, row.name?.trim() || row.email]))
+ }
+
+ if (dimension === 'workspace') {
+ const rows = await executor
+ .select({ id: workspace.id, name: workspace.name })
+ .from(workspace)
+ .where(inArray(workspace.id, ids))
+ return new Map(rows.map((row) => [row.id, row.name]))
+ }
+
+ if (dimension === 'workflow') {
+ const rows = await executor
+ .select({ id: workflow.id, name: workflow.name })
+ .from(workflow)
+ .where(inArray(workflow.id, ids))
+ return new Map(rows.map((row) => [row.id, row.name]))
+ }
+
+ return new Map()
+}
diff --git a/apps/sim/lib/billing/core/usage-analytics.test.ts b/apps/sim/lib/billing/core/usage-analytics.test.ts
new file mode 100644
index 00000000000..c288c8e62ec
--- /dev/null
+++ b/apps/sim/lib/billing/core/usage-analytics.test.ts
@@ -0,0 +1,542 @@
+/**
+ * @vitest-environment node
+ */
+import { describe, expect, it } from 'vitest'
+import type { ResolvedUsagePeriod } from '@/lib/billing/core/reporting-period'
+import {
+ buildUsageAnalyticsScope,
+ densifyUsageSeries,
+ foldUsageBreakdown,
+ MAX_CUSTOM_RANGE_DAYS,
+ mergeRowsByKey,
+ resolvePreviousPeriod,
+ resolveUsageAnalyticsWindow,
+ resolveUsageBucket,
+ UsageWindowRangeInvertedError,
+ UsageWindowRangeTooLargeError,
+ usageWindowLedgerFilter,
+} from '@/lib/billing/core/usage-analytics'
+
+const ENTITY = { type: 'organization', id: 'org-1' } as const
+
+function period(overrides: Partial = {}): ResolvedUsagePeriod {
+ return {
+ start: new Date('2026-08-01T00:00:00.000Z'),
+ end: new Date('2026-09-01T00:00:00.000Z'),
+ source: 'stripe',
+ anchorDate: null,
+ interval: null,
+ ...overrides,
+ }
+}
+
+/** The generated predicate as SQL-ish text, for asserting which columns it filters. */
+function scopeShape(window: Parameters[1]): string {
+ return JSON.stringify(buildUsageAnalyticsScope(ENTITY, window))
+}
+
+describe('buildUsageAnalyticsScope', () => {
+ it('matches a reporting period on created_at, not on the stamps', () => {
+ // A reporting period is derived from an anchor date and is not what rows carry,
+ // so matching the stamps would return nothing for every enterprise org.
+ const shape = scopeShape({
+ kind: 'period',
+ period: period({ source: 'reporting', anchorDate: '2026-08-01', interval: 'month' }),
+ })
+ expect(shape).toContain('usageLog.createdAt')
+ expect(shape).not.toContain('usageLog.billingPeriodStart')
+ })
+
+ it('matches a stripe period on the exact stamps', () => {
+ const shape = scopeShape({ kind: 'period', period: period({ source: 'stripe' }) })
+ expect(shape).toContain('usageLog.billingPeriodStart')
+ expect(shape).toContain('usageLog.billingPeriodEnd')
+ })
+
+ it('matches a plain range on created_at', () => {
+ const shape = scopeShape({
+ kind: 'range',
+ from: new Date('2026-08-01T00:00:00.000Z'),
+ to: new Date('2026-08-08T00:00:00.000Z'),
+ })
+ expect(shape).toContain('usageLog.createdAt')
+ expect(shape).not.toContain('usageLog.billingPeriodStart')
+ })
+
+ it('always scopes to the billing entity', () => {
+ const shape = scopeShape({ kind: 'period', period: period() })
+ expect(shape).toContain('usageLog.billingEntityType')
+ expect(shape).toContain('usageLog.billingEntityId')
+ })
+})
+
+describe('usageWindowLedgerFilter', () => {
+ it('matches a stripe period on the stamps, as the aggregate does', () => {
+ // Filtering this window on `created_at` instead selects a different set — rows
+ // created inside the period but stamped to another, and vice versa — so the event
+ // list and the CSV covered different rows than the totals above them.
+ const filter = usageWindowLedgerFilter({ kind: 'period', period: period({ source: 'stripe' }) })
+ expect(filter).toEqual({
+ billingPeriod: {
+ start: new Date('2026-08-01T00:00:00.000Z'),
+ end: new Date('2026-09-01T00:00:00.000Z'),
+ },
+ })
+ })
+
+ it('matches a reporting period on created_at, as the aggregate does', () => {
+ const filter = usageWindowLedgerFilter({
+ kind: 'period',
+ period: period({ source: 'reporting', anchorDate: '2026-08-01', interval: 'month' }),
+ })
+ expect(filter.billingPeriod).toBeUndefined()
+ expect(filter.endDateExclusive).toBe(true)
+ })
+
+ it('keeps a plain range half-open', () => {
+ const from = new Date('2026-08-01T00:00:00.000Z')
+ const to = new Date('2026-08-08T00:00:00.000Z')
+ expect(usageWindowLedgerFilter({ kind: 'range', from, to })).toEqual({
+ startDate: from,
+ endDate: to,
+ endDateExclusive: true,
+ })
+ })
+
+ it('branches on the same condition the scope builder does', () => {
+ // The two predicates are only in step because they read the same discriminant.
+ for (const source of ['reporting', 'stripe', 'default'] as const) {
+ const window = { kind: 'period' as const, period: period({ source }) }
+ const usesStamps = scopeShape(window).includes('usageLog.billingPeriodStart')
+ expect(usageWindowLedgerFilter(window).billingPeriod !== undefined).toBe(usesStamps)
+ }
+ })
+})
+
+describe('resolveUsageAnalyticsWindow', () => {
+ const now = new Date('2026-08-20T12:00:00.000Z')
+
+ it('keeps current-period a period so it matches the billing page', () => {
+ const window = resolveUsageAnalyticsWindow({ preset: 'current-period', period: period(), now })
+ expect(window).toEqual({ kind: 'period', period: period() })
+ })
+
+ it('derives the previous reporting period from its anchor', () => {
+ const window = resolveUsageAnalyticsWindow({
+ preset: 'previous-period',
+ period: period({ source: 'reporting', anchorDate: '2026-01-15', interval: 'month' }),
+ now,
+ })
+ expect(window.kind).toBe('period')
+ })
+
+ it('falls back to an equal-length range when the previous period is not derivable', () => {
+ // A stripe period's predecessor lives in Stripe; inventing stamps would match nothing.
+ const window = resolveUsageAnalyticsWindow({
+ preset: 'previous-period',
+ period: period({ source: 'stripe' }),
+ now,
+ })
+ expect(window.kind).toBe('range')
+ if (window.kind === 'range') {
+ expect(window.to).toEqual(new Date('2026-08-01T00:00:00.000Z'))
+ expect(window.from).toEqual(new Date('2026-07-01T00:00:00.000Z'))
+ }
+ })
+
+ it('makes a custom range half-open so a single day returns that day', () => {
+ const window = resolveUsageAnalyticsWindow({
+ preset: 'custom',
+ period: period(),
+ customStart: new Date('2026-08-04T00:00:00.000Z'),
+ customEnd: new Date('2026-08-04T00:00:00.000Z'),
+ now,
+ })
+ expect(window).toEqual({
+ kind: 'range',
+ from: new Date('2026-08-04T00:00:00.000Z'),
+ to: new Date('2026-08-05T00:00:00.000Z'),
+ })
+ })
+
+ it('covers exactly the days the picker emitted, from its bare date bounds', () => {
+ // The picker sends `YYYY-MM-DD`, which parses as UTC midnight. It must not send
+ // an inclusive `…T23:59:59` wall time: the extra day added below would then land
+ // on the *following* day, and every custom range would overrun by 24 hours.
+ const window = resolveUsageAnalyticsWindow({
+ preset: 'custom',
+ period: period(),
+ customStart: new Date('2026-06-01'),
+ customEnd: new Date('2026-08-31'),
+ now,
+ })
+ expect(window).toEqual({
+ kind: 'range',
+ from: new Date('2026-06-01T00:00:00.000Z'),
+ to: new Date('2026-09-01T00:00:00.000Z'),
+ })
+ })
+
+ it('accepts a selection of exactly the maximum span', () => {
+ // June 1 through August 31 inclusive is 92 days. It measured 93 while the end
+ // bound carried a time of day, so the longest legal pick was rejected.
+ expect(() =>
+ resolveUsageAnalyticsWindow({
+ preset: 'custom',
+ period: period(),
+ customStart: new Date('2026-06-01'),
+ customEnd: new Date('2026-08-31'),
+ now,
+ })
+ ).not.toThrow()
+ })
+
+ it('refuses a custom range beyond the cap rather than scanning the ledger', () => {
+ expect(() =>
+ resolveUsageAnalyticsWindow({
+ preset: 'custom',
+ period: period(),
+ customStart: new Date('2026-01-01T00:00:00.000Z'),
+ customEnd: new Date('2026-08-01T00:00:00.000Z'),
+ now,
+ })
+ ).toThrow(UsageWindowRangeTooLargeError)
+ })
+
+ it('falls back to the period when a custom range is missing a bound', () => {
+ const window = resolveUsageAnalyticsWindow({ preset: 'custom', period: period(), now })
+ expect(window.kind).toBe('period')
+ })
+
+ it('anchors custom bounds on midnight in the viewer calendar, not UTC', () => {
+ // The picker offers calendar days. Anchoring on the UTC instant shifted every
+ // non-UTC viewer's selection by their offset, and disagreed with the chart,
+ // whose buckets are already the viewer's calendar days.
+ const window = resolveUsageAnalyticsWindow({
+ preset: 'custom',
+ period: period(),
+ customStart: new Date('2026-08-01'),
+ customEnd: new Date('2026-08-31'),
+ timezone: 'America/New_York',
+ now,
+ })
+ expect(window.kind).toBe('range')
+ if (window.kind === 'range') {
+ // Midnight on Aug 1 in New York is 04:00 UTC (EDT, UTC-4).
+ expect(window.from.toISOString()).toBe('2026-08-01T04:00:00.000Z')
+ expect(window.to.toISOString()).toBe('2026-09-01T04:00:00.000Z')
+ }
+ })
+
+ it('refuses a range that ends before it starts', () => {
+ // Inverted bounds measured a negative span, passed the cap check, and produced a
+ // range that matched nothing — indistinguishable from "no usage".
+ expect(() =>
+ resolveUsageAnalyticsWindow({
+ preset: 'custom',
+ period: period(),
+ customStart: new Date('2026-08-31'),
+ customEnd: new Date('2026-08-01'),
+ now,
+ })
+ ).toThrow(UsageWindowRangeInvertedError)
+ })
+
+ it('shows a bounded window for a deployment with no subscription', () => {
+ // `defaultBillingPeriod()` is the open pair 1970…9999. Rendered as a period it
+ // produced 1,000 monthly buckets ending in 2053, stopped only by the densifier's
+ // loop guard — reachable on self-hosted, where the panel opens without a plan.
+ const window = resolveUsageAnalyticsWindow({
+ preset: 'current-period',
+ period: period({
+ source: 'default',
+ start: new Date(0),
+ end: new Date(Date.UTC(9999, 11, 31)),
+ }),
+ now,
+ })
+ expect(window.kind).toBe('range')
+ if (window.kind === 'range') {
+ expect(window.to).toEqual(now)
+ expect(Math.round((window.to.getTime() - window.from.getTime()) / 86_400_000)).toBe(30)
+ }
+ })
+
+ it('steps an unbounded period back by the display window, not by its own length', () => {
+ const window = resolveUsageAnalyticsWindow({
+ preset: 'previous-period',
+ period: period({
+ source: 'default',
+ start: new Date(0),
+ end: new Date(Date.UTC(9999, 11, 31)),
+ }),
+ now,
+ })
+ expect(window.kind).toBe('range')
+ if (window.kind === 'range') {
+ // Not 1970-minus-eight-millennia, which is what deriving it from the span gave.
+ expect(window.from.getUTCFullYear()).toBe(2026)
+ }
+ })
+})
+
+describe('resolvePreviousPeriod', () => {
+ it('returns null for a period with no derivation rule', () => {
+ expect(resolvePreviousPeriod(period({ source: 'stripe' }))).toBeNull()
+ expect(resolvePreviousPeriod(period({ source: 'default' }))).toBeNull()
+ })
+})
+
+describe('resolveUsageBucket', () => {
+ const from = new Date('2026-01-01T00:00:00.000Z')
+ const days = (n: number) => new Date(from.getTime() + n * 24 * 60 * 60 * 1000)
+
+ it('scales granularity with the window', () => {
+ expect(resolveUsageBucket({ kind: 'range', from, to: days(30) })).toBe('day')
+ expect(resolveUsageBucket({ kind: 'range', from, to: days(200) })).toBe('week')
+ expect(resolveUsageBucket({ kind: 'range', from, to: days(500) })).toBe('month')
+ })
+
+ it('keeps daily bars for the maximum range across a DST transition', () => {
+ // 92 calendar days spanning the autumn fall-back is 92 days and one hour. Ceiling
+ // that called it 93 and silently demoted the longest legal custom range to weekly
+ // bars — a granularity change with no cause the reader could see.
+ const dstSpan = {
+ kind: 'range' as const,
+ from,
+ to: new Date(from.getTime() + 92 * 86_400_000 + 3_600_000),
+ }
+ expect(resolveUsageBucket(dstSpan)).toBe('day')
+ // A genuinely longer range still steps up.
+ expect(
+ resolveUsageBucket({ kind: 'range', from, to: new Date(from.getTime() + 93 * 86_400_000) })
+ ).toBe('week')
+ })
+})
+
+describe('densifyUsageSeries', () => {
+ const window = {
+ kind: 'range' as const,
+ from: new Date('2026-08-01T00:00:00.000Z'),
+ to: new Date('2026-08-04T00:00:00.000Z'),
+ }
+
+ it('fills empty buckets with zero rather than omitting them', () => {
+ // A period with no usage must draw a flat zero line; "No data" reads as a failure.
+ const points = densifyUsageSeries(
+ [{ bucketStart: '2026-08-02T00:00:00', cost: '1.50', events: 3 }],
+ window,
+ 'day',
+ 'UTC'
+ )
+ expect(points).toHaveLength(3)
+ expect(points.map((p) => p.cost)).toEqual([0, 1.5, 0])
+ expect(points.map((p) => p.events)).toEqual([0, 3, 0])
+ })
+
+ it('preserves the window total', () => {
+ const points = densifyUsageSeries(
+ [
+ { bucketStart: '2026-08-01T00:00:00', cost: '1.25', events: 1 },
+ { bucketStart: '2026-08-03T00:00:00', cost: '2.75', events: 2 },
+ ],
+ window,
+ 'day',
+ 'UTC'
+ )
+ expect(points.reduce((sum, p) => sum + p.cost, 0)).toBeCloseTo(4, 8)
+ })
+
+ it('emits an empty series for a zero-length window instead of looping', () => {
+ expect(
+ densifyUsageSeries([], { kind: 'range', from: window.from, to: window.from }, 'day', 'UTC')
+ ).toEqual([])
+ })
+
+ it('keys days by the viewer calendar the query grouped by, not UTC', () => {
+ // Auckland is UTC+12, so the window's final instant is already the next local
+ // day. Walking UTC dates dropped that bucket: its cost stayed in the headline
+ // while its bar was never drawn.
+ const points = densifyUsageSeries(
+ [{ bucketStart: '2026-08-04T00:00:00', cost: '5.00', events: 1 }],
+ window,
+ 'day',
+ 'Pacific/Auckland'
+ )
+ const keys = points.map((p) => p.timestamp.slice(0, 10))
+ expect(keys).toEqual(['2026-08-01', '2026-08-02', '2026-08-03', '2026-08-04'])
+ expect(points.at(-1)?.cost).toBe(5)
+ })
+
+ it('aligns week buckets to Monday, as `date_trunc` does', () => {
+ // A period starting mid-week previously produced keys Postgres never emits, so
+ // every bar read zero while the total was correct. Reachable through an annual
+ // enterprise period, which resolves to `week`.
+ const points = densifyUsageSeries(
+ [{ bucketStart: '2026-08-10T00:00:00', cost: '9.00', events: 4 }],
+ // 2026-08-15 is a Saturday; its ISO week starts Monday 2026-08-10.
+ {
+ kind: 'range',
+ from: new Date('2026-08-15T00:00:00.000Z'),
+ to: new Date('2026-08-29T00:00:00.000Z'),
+ },
+ 'week',
+ 'UTC'
+ )
+ expect(points.map((p) => p.timestamp.slice(0, 10))).toEqual([
+ '2026-08-10',
+ '2026-08-17',
+ '2026-08-24',
+ ])
+ expect(points[0].cost).toBe(9)
+ })
+
+ it('aligns month buckets to the first, as `date_trunc` does', () => {
+ const points = densifyUsageSeries(
+ [{ bucketStart: '2026-09-01T00:00:00', cost: '3.00', events: 2 }],
+ {
+ kind: 'range',
+ from: new Date('2026-08-15T00:00:00.000Z'),
+ to: new Date('2026-10-15T00:00:00.000Z'),
+ },
+ 'month',
+ 'UTC'
+ )
+ expect(points.map((p) => p.timestamp.slice(0, 10))).toEqual([
+ '2026-08-01',
+ '2026-09-01',
+ '2026-10-01',
+ ])
+ expect(points[1].cost).toBe(3)
+ })
+})
+
+describe('foldUsageBreakdown', () => {
+ const rows = [
+ { key: 'a', cost: '5', events: 5 },
+ { key: 'b', cost: '3', events: 3 },
+ { key: 'c', cost: '2', events: 2 },
+ ]
+ const labelFor = (key: string | null) => key ?? 'Unattributed'
+
+ it('reconciles visible rows plus the remainder to the total', () => {
+ const fold = foldUsageBreakdown(rows, 10, labelFor, 2)
+ const visible = fold.rows.reduce((sum, row) => sum + row.cost, 0)
+ expect(visible + fold.other.cost).toBeCloseTo(fold.totalCost, 8)
+ expect(fold.other.rowCount).toBe(1)
+ })
+
+ it('ranks by cost descending', () => {
+ expect(foldUsageBreakdown(rows, 10, labelFor, 3).rows.map((r) => r.id)).toEqual(['a', 'b', 'c'])
+ })
+
+ it('computes share against the window total, not the visible subset', () => {
+ // Sharing against the visible rows would make a truncated list read as 100%.
+ const fold = foldUsageBreakdown(rows, 10, labelFor, 1)
+ expect(fold.rows[0].share).toBeCloseTo(0.5, 8)
+ })
+
+ it('labels a null grouping key rather than dropping the row', () => {
+ const fold = foldUsageBreakdown([{ key: null, cost: '4', events: 1 }], 4, labelFor, 5)
+ expect(fold.rows[0].label).toBe('Unattributed')
+ })
+
+ it('yields zero shares when nothing was spent, without dividing by zero', () => {
+ const fold = foldUsageBreakdown([{ key: 'a', cost: '0', events: 2 }], 0, labelFor, 5)
+ expect(fold.rows[0].share).toBe(0)
+ })
+
+ it('measures share in the ranking unit, so a zero-cost list still draws bars', () => {
+ // BYOK ranks by a cost that is zero for every row, so a cost-based share is 0/0
+ // for all of them and every bar renders at the same minimum width.
+ const byokRows = [
+ { key: 'gpt-4o', cost: '0', events: 1, inputTokens: 100, outputTokens: 50 },
+ { key: 'claude', cost: '0', events: 1, inputTokens: 700, outputTokens: 300 },
+ { key: 'gemini', cost: '0', events: 1, inputTokens: 20, outputTokens: 5 },
+ ]
+ const fold = foldUsageBreakdown(byokRows, 0, labelFor, 3, 'tokens')
+ // Share is measured in the ranking unit, or every BYOK bar renders identical:
+ // a cost-based share is 0/0 for every provider.
+ expect(fold.rows.map((row) => row.share)).toEqual([1000 / 1175, 150 / 1175, 25 / 1175])
+ })
+
+ it('carries the omitted rows tokens, so a zero-cost dimension still adds up', () => {
+ const byokRows = [
+ { key: 'gpt-4o', cost: '0', events: 1, inputTokens: 100, outputTokens: 50 },
+ { key: 'claude', cost: '0', events: 1, inputTokens: 700, outputTokens: 300 },
+ { key: 'gemini', cost: '0', events: 1, inputTokens: 20, outputTokens: 5 },
+ ]
+ const fold = foldUsageBreakdown(byokRows, 0, labelFor, 1, 'tokens')
+ // Ranked by tokens, so the biggest provider is the one shown...
+ expect(fold.rows.map((row) => row.id)).toEqual(['claude'])
+ // ...and the tail's tokens are still accounted for rather than hidden.
+ expect(fold.other.rowCount).toBe(2)
+ expect(fold.other.tokens).toBe(175)
+ })
+})
+
+describe('MAX_CUSTOM_RANGE_DAYS', () => {
+ it('is the documented cap', () => {
+ expect(MAX_CUSTOM_RANGE_DAYS).toBe(92)
+ })
+})
+
+describe('mergeRowsByKey', () => {
+ it('collapses sources that share a display label', () => {
+ // The ledger stores `copilot` and `workspace-chat` separately but both render as
+ // "Sim Chat", so grouping on the raw column alone shipped the label twice with the
+ // usage split between the rows — and ranked both below their true position.
+ const merged = mergeRowsByKey(
+ [
+ { key: 'copilot', cost: '0.0295', events: 2 },
+ { key: 'workspace-chat', cost: '23.3967', events: 40 },
+ { key: 'workflow', cost: '0.9333', events: 130 },
+ ],
+ (key) => (key === 'copilot' || key === 'workspace-chat' ? 'sim-chat' : key)
+ )
+
+ expect(merged).toHaveLength(2)
+ const simChat = merged.find((row) => row.key === 'sim-chat')
+ expect(Number(simChat?.cost)).toBeCloseTo(23.4262, 8)
+ expect(simChat?.events).toBe(42)
+ })
+
+ it('sums token columns alongside cost', () => {
+ const merged = mergeRowsByKey(
+ [
+ { key: 'claude-opus-4.8', cost: '1', events: 1, inputTokens: 100, outputTokens: 10 },
+ { key: 'claude-sonnet-4', cost: '2', events: 3, inputTokens: 50, outputTokens: 5 },
+ ],
+ () => 'anthropic'
+ )
+
+ expect(merged).toEqual([
+ { key: 'anthropic', cost: 3, events: 4, inputTokens: 150, outputTokens: 15 },
+ ])
+ })
+
+ it('preserves a null key rather than merging it into a named row', () => {
+ const merged = mergeRowsByKey(
+ [
+ { key: null, cost: '1', events: 1 },
+ { key: 'a', cost: '2', events: 1 },
+ ],
+ (key) => key
+ )
+ expect(merged.map((row) => row.key)).toEqual([null, 'a'])
+ })
+
+ it('leaves totals unchanged', () => {
+ const rows = [
+ { key: 'a', cost: '1.5', events: 1 },
+ { key: 'b', cost: '2.5', events: 2 },
+ { key: 'c', cost: '3', events: 3 },
+ ]
+ const before = rows.reduce((sum, row) => sum + Number(row.cost), 0)
+ const after = mergeRowsByKey(rows, (key) => (key === 'c' ? 'c' : 'ab')).reduce(
+ (sum, row) => sum + Number(row.cost),
+ 0
+ )
+ expect(after).toBeCloseTo(before, 8)
+ })
+})
diff --git a/apps/sim/lib/billing/core/usage-analytics.ts b/apps/sim/lib/billing/core/usage-analytics.ts
new file mode 100644
index 00000000000..07533fe0ac6
--- /dev/null
+++ b/apps/sim/lib/billing/core/usage-analytics.ts
@@ -0,0 +1,571 @@
+import { usageLog } from '@sim/db/schema'
+import { eq, gte, lt, type SQL } from 'drizzle-orm'
+import { MAX_CUSTOM_RANGE_DAYS } from '@/lib/api/contracts/organization-usage'
+import {
+ type ResolvedUsagePeriod,
+ resolveEnterpriseReportingPeriod,
+} from '@/lib/billing/core/reporting-period'
+import type { BillingEntity } from '@/lib/billing/core/usage-log'
+import { zonedWallClockToUtc } from '@/lib/core/utils/timezone'
+
+/**
+ * Pure half of organization usage analytics: window resolution, the ledger scope
+ * every query is built from, bucket granularity, and the folds that turn sparse
+ * rows into dense series and ranked lists.
+ *
+ * No DB access, so the parts most likely to be wrong — period semantics and
+ * reconciliation arithmetic — are directly testable.
+ */
+
+export const USAGE_WINDOW_PRESETS = [
+ 'current-period',
+ 'previous-period',
+ '7d',
+ '30d',
+ 'custom',
+] as const
+export type UsageWindowPreset = (typeof USAGE_WINDOW_PRESETS)[number]
+
+export const USAGE_BREAKDOWN_DIMENSIONS = [
+ 'member',
+ 'workspace',
+ 'workflow',
+ 'model',
+ 'byok',
+ 'source',
+] as const
+export type UsageBreakdownDimension = (typeof USAGE_BREAKDOWN_DIMENSIONS)[number]
+
+export type UsageBucket = 'day' | 'week' | 'month'
+
+/**
+ * A custom range is capped because three of the five breakdown dimensions are not
+ * index-covered and heap-fetch per row; an unbounded range over a large ledger is a
+ * table scan. Longer look-back goes through `previous-period`, which is stamped.
+ *
+ * Declared on the contract so the picker and this resolver state one number.
+ */
+export { MAX_CUSTOM_RANGE_DAYS }
+
+const DAY_MS = 24 * 60 * 60 * 1000
+
+export type UsageAnalyticsWindow =
+ | { kind: 'period'; period: ResolvedUsagePeriod }
+ | { kind: 'range'; from: Date; to: Date }
+
+/**
+ * The ledger predicate every usage query is built from.
+ *
+ * This is the one place the `reporting` branch is written for this feature, and it
+ * mirrors `getBillingPeriodUsageCost` deliberately: a reporting period is derived
+ * from an anchor date and is *not* what rows are stamped with, so it matches on
+ * `created_at`; a stripe/default period matches the stamps exactly. Diverging here
+ * is how the usage panel would come to disagree with the billing page about the
+ * same period.
+ */
+export function buildUsageAnalyticsScope(
+ entity: BillingEntity,
+ window: UsageAnalyticsWindow
+): SQL[] {
+ const conditions: SQL[] = [
+ eq(usageLog.billingEntityType, entity.type),
+ eq(usageLog.billingEntityId, entity.id),
+ ]
+
+ if (window.kind === 'range') {
+ conditions.push(gte(usageLog.createdAt, window.from), lt(usageLog.createdAt, window.to))
+ return conditions
+ }
+
+ if (window.period.source === 'reporting') {
+ conditions.push(
+ gte(usageLog.createdAt, window.period.start),
+ lt(usageLog.createdAt, window.period.end)
+ )
+ return conditions
+ }
+
+ conditions.push(
+ eq(usageLog.billingPeriodStart, window.period.start),
+ eq(usageLog.billingPeriodEnd, window.period.end)
+ )
+ return conditions
+}
+
+/** The instants a window covers, for labelling and for deriving bucket granularity. */
+export function usageWindowBounds(window: UsageAnalyticsWindow): { start: Date; end: Date } {
+ return window.kind === 'range'
+ ? { start: window.from, end: window.to }
+ : { start: window.period.start, end: window.period.end }
+}
+
+export interface UsageWindowLedgerFilter {
+ startDate?: Date
+ endDate?: Date
+ endDateExclusive?: boolean
+ billingPeriod?: { start: Date; end: Date }
+}
+
+/**
+ * The same window, expressed for the ledger listing query.
+ *
+ * `getBillingEntityUsageLogs` filters rows while {@link buildUsageAnalyticsScope}
+ * aggregates them, and the two must select the same set or the event list and CSV
+ * describe different rows than the totals above them. That is not hypothetical for a
+ * stripe or default period: those are matched on the *stamps* rows carry, and
+ * filtering the same window on `created_at` picks up rows created inside it but
+ * stamped to another period, while missing the reverse.
+ *
+ * Deriving both from one function is what keeps the predicates in step — the branches
+ * below mirror `buildUsageAnalyticsScope` case for case.
+ */
+export function usageWindowLedgerFilter(window: UsageAnalyticsWindow): UsageWindowLedgerFilter {
+ if (window.kind === 'range' || window.period.source === 'reporting') {
+ const { start, end } = usageWindowBounds(window)
+ // Half-open, so a row on the boundary belongs to the next window — as it does
+ // for the aggregate, whose `lt` says the same thing.
+ return { startDate: start, endDate: end, endDateExclusive: true }
+ }
+ return { billingPeriod: { start: window.period.start, end: window.period.end } }
+}
+
+export class UsageWindowRangeTooLargeError extends Error {
+ constructor(days: number) {
+ super(`Custom range spans ${days} days; the maximum is ${MAX_CUSTOM_RANGE_DAYS}.`)
+ this.name = 'UsageWindowRangeTooLargeError'
+ }
+}
+
+export class UsageWindowRangeInvertedError extends Error {
+ constructor() {
+ super('Custom range ends before it starts.')
+ this.name = 'UsageWindowRangeInvertedError'
+ }
+}
+
+/**
+ * How much of an unbounded period to show.
+ *
+ * A deployment with no subscription resolves to `defaultBillingPeriod()`, which is
+ * the open pair `1970-01-01 … 9999-12-31`. Rendered as a period that is 1,000
+ * monthly buckets ending in 2053, stopped only by the densifier's loop guard — the
+ * chart was unusable and the label claimed it was a billing period. Self-hosted is
+ * exactly where this is reachable, since `USAGE_MONITORING_ENABLED` opens the panel
+ * on deployments that have no subscription at all.
+ */
+const UNBOUNDED_PERIOD_DISPLAY_DAYS = 30
+
+/** True for the open sentinel period a deployment without a subscription resolves to. */
+function isUnboundedPeriod(period: ResolvedUsagePeriod): boolean {
+ return period.source === 'default'
+}
+
+interface ResolveUsageWindowArgs {
+ preset: UsageWindowPreset
+ /** The payer's current period, already resolved from its subscription. */
+ period: ResolvedUsagePeriod
+ customStart?: Date
+ customEnd?: Date
+ /**
+ * The viewer's calendar, used to resolve date-only custom bounds. The picker
+ * offers calendar days, so "Aug 31" has to mean midnight-to-midnight *there*.
+ */
+ timezone?: string
+ now?: Date
+}
+
+/** The civil date an already-UTC-parsed `YYYY-MM-DD` bound represents. */
+function civilBoundKey(bound: Date): string {
+ return bound.toISOString().slice(0, 10)
+}
+
+/** Exact whole-day distance between two civil dates; unaffected by DST. */
+function civilDaysBetween(fromKey: string, toKey: string): number {
+ return Math.round((civilDate(toKey).getTime() - civilDate(fromKey).getTime()) / DAY_MS)
+}
+
+/**
+ * Maps a picker selection to a window the ledger can actually match.
+ *
+ * `current-period` and `previous-period` stay *periods* so they use the same
+ * predicate the billing page does; the rolling and custom presets are plain
+ * `created_at` ranges.
+ */
+export function resolveUsageAnalyticsWindow({
+ preset,
+ period,
+ customStart,
+ customEnd,
+ timezone = 'UTC',
+ now = new Date(),
+}: ResolveUsageWindowArgs): UsageAnalyticsWindow {
+ switch (preset) {
+ case 'current-period':
+ return isUnboundedPeriod(period)
+ ? {
+ kind: 'range',
+ from: new Date(now.getTime() - UNBOUNDED_PERIOD_DISPLAY_DAYS * DAY_MS),
+ to: now,
+ }
+ : { kind: 'period', period }
+ case 'previous-period': {
+ const previous = resolvePreviousPeriod(period)
+ if (previous) return { kind: 'period', period: previous }
+ // An open period has no meaningful predecessor — deriving one from its length
+ // reaches back eight millennia — so it steps back by the display window instead.
+ if (isUnboundedPeriod(period)) {
+ const to = new Date(now.getTime() - UNBOUNDED_PERIOD_DISPLAY_DAYS * DAY_MS)
+ return {
+ kind: 'range',
+ from: new Date(to.getTime() - UNBOUNDED_PERIOD_DISPLAY_DAYS * DAY_MS),
+ to,
+ }
+ }
+ // A stripe period carries no rule for deriving its predecessor, so fall back to
+ // a range of the same length rather than inventing stamps that would match
+ // nothing. This is an approximation, which is why it is never used as the
+ // summary's comparison window — see `resolvePreviousPeriod` there.
+ return {
+ kind: 'range',
+ from: new Date(period.start.getTime() - (period.end.getTime() - period.start.getTime())),
+ to: period.start,
+ }
+ }
+ case '7d':
+ return { kind: 'range', from: new Date(now.getTime() - 7 * DAY_MS), to: now }
+ case '30d':
+ return { kind: 'range', from: new Date(now.getTime() - 30 * DAY_MS), to: now }
+ case 'custom': {
+ // A partial selection is not a range, so it falls back to the current period —
+ // through the same branch, which is what keeps an unbounded period from being
+ // scanned in full here as well.
+ if (!customStart || !customEnd) {
+ return resolveUsageAnalyticsWindow({ preset: 'current-period', period, now })
+ }
+ /**
+ * The picker offers calendar days and sends `YYYY-MM-DD`, which arrives here
+ * parsed as UTC midnight. Anchoring the window on those instants shifted every
+ * non-UTC viewer's selection by their offset — a range labelled "Aug 1–31"
+ * covered half of Jul 31 and half of Aug 31 for a viewer twelve hours east,
+ * and disagreed with the chart, whose buckets are already the viewer's
+ * calendar days. Reinterpret the same civil dates as midnight *there*.
+ */
+ const startKey = civilBoundKey(customStart)
+ const endKey = civilBoundKey(customEnd)
+ // Guarded before the span check, which would otherwise measure a negative
+ // number of days, pass the cap, and return an inverted range that matches no
+ // rows — reading as "no usage" rather than as a bad request.
+ if (endKey < startKey) throw new UsageWindowRangeInvertedError()
+
+ const days = civilDaysBetween(startKey, endKey) + 1
+ if (days > MAX_CUSTOM_RANGE_DAYS) throw new UsageWindowRangeTooLargeError(days)
+
+ const exclusiveEndKey = civilKey(
+ (() => {
+ const cursor = civilDate(endKey)
+ cursor.setUTCDate(cursor.getUTCDate() + 1)
+ return cursor
+ })()
+ )
+ return {
+ kind: 'range',
+ from: zonedWallClockToUtc(`${startKey}T00:00`, timezone),
+ to: zonedWallClockToUtc(`${exclusiveEndKey}T00:00`, timezone),
+ }
+ }
+ }
+}
+
+/**
+ * The period immediately before this one, or `null` when it is not exactly
+ * derivable. Only a reporting period has a rule (its anchor); a stripe period's
+ * predecessor lives in Stripe, and guessing it would silently compare against the
+ * wrong window.
+ */
+export function resolvePreviousPeriod(period: ResolvedUsagePeriod): ResolvedUsagePeriod | null {
+ if (period.source !== 'reporting' || !period.anchorDate || !period.interval) return null
+ return resolveEnterpriseReportingPeriod(
+ period.anchorDate,
+ period.interval,
+ new Date(period.start.getTime() - 1)
+ )
+}
+
+/**
+ * Bucket width derived from the window rather than requested.
+ *
+ * Calendar-aligned on purpose: a billing period starts at an arbitrary instant, so
+ * epoch-modulo buckets would cut every day mid-afternoon and each bar would straddle
+ * two calendar days. Spend is read against the calendar.
+ */
+export function resolveUsageBucket(window: UsageAnalyticsWindow): UsageBucket {
+ const { start, end } = usageWindowBounds(window)
+ // Rounded, not ceiled: a 92-day range spanning the autumn transition is 92 days and
+ // one hour, which `ceil` called 93 — quietly demoting the longest legal custom range
+ // from daily bars to weekly ones.
+ const days = Math.max(1, Math.round((end.getTime() - start.getTime()) / DAY_MS))
+ if (days <= 92) return 'day'
+ if (days <= 400) return 'week'
+ return 'month'
+}
+
+export interface UsageSeriesPoint {
+ timestamp: string
+ cost: number
+ events: number
+}
+
+interface SparseBucketRow {
+ bucketStart: string | null
+ cost: string | number | null
+ events: number | string | null
+}
+
+function toNumber(value: string | number | null | undefined): number {
+ if (typeof value === 'number') return Number.isFinite(value) ? value : 0
+ const parsed = Number.parseFloat(value ?? '0')
+ return Number.isFinite(parsed) ? parsed : 0
+}
+
+/** The `YYYY-MM-DD` an instant falls on in the viewer's calendar — what `AT TIME ZONE` produced. */
+function localCalendarDate(instant: Date, timezone: string): string {
+ return new Intl.DateTimeFormat('en-CA', {
+ timeZone: timezone,
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ }).format(instant)
+}
+
+/**
+ * Civil-date arithmetic on a `YYYY-MM-DD` key.
+ *
+ * UTC is used purely as a proleptic calendar here — these values are never converted
+ * back to an instant, so no offset or DST transition can shift them. Doing the same
+ * arithmetic on a real local instant is what would break across a DST boundary.
+ */
+function civilDate(key: string): Date {
+ return new Date(`${key}T00:00:00.000Z`)
+}
+
+function civilKey(date: Date): string {
+ return date.toISOString().slice(0, 10)
+}
+
+/**
+ * Mirrors Postgres `date_trunc(bucket, …)`: an ISO week starts Monday, a month on
+ * the 1st. The series keys have to land on the same boundaries the SQL emitted or
+ * no lookup below will ever hit.
+ */
+function truncateToBucket(key: string, bucket: UsageBucket): string {
+ const date = civilDate(key)
+ if (bucket === 'week') date.setUTCDate(date.getUTCDate() - ((date.getUTCDay() + 6) % 7))
+ else if (bucket === 'month') date.setUTCDate(1)
+ return civilKey(date)
+}
+
+/**
+ * Fills every bucket in the window, because SQL only returns buckets that have rows.
+ *
+ * A period with no usage must render a flat zero line, not the chart's "No data"
+ * branch — zero is information, "No data" reads as a failure.
+ *
+ * The keys are generated in the *same calendar the query grouped by*:
+ * `readUsageTimeSeries` truncates `created_at AT TIME ZONE `, so a viewer
+ * east or west of UTC buckets rows by their own calendar date. Walking UTC dates
+ * here instead dropped the edge buckets of every non-UTC window — their cost stayed
+ * in the headline while their bar read zero. Week and month were worse than an edge
+ * case: Postgres aligns those to Monday and the 1st, so a cursor stepping from an
+ * arbitrary period start shared no key with the query at all and the whole chart
+ * came back zeroed. That is reachable today through an annual enterprise period,
+ * which resolves to `week`.
+ */
+export function densifyUsageSeries(
+ rows: SparseBucketRow[],
+ window: UsageAnalyticsWindow,
+ bucket: UsageBucket,
+ timezone: string
+): UsageSeriesPoint[] {
+ const byBucket = new Map()
+ for (const row of rows) {
+ if (row.bucketStart) byBucket.set(row.bucketStart.slice(0, 10), row)
+ }
+
+ const { start, end } = usageWindowBounds(window)
+ const first = truncateToBucket(localCalendarDate(start, timezone), bucket)
+ // The window is half-open, so the last bucket is the one holding its final instant.
+ const last = truncateToBucket(localCalendarDate(new Date(end.getTime() - 1), timezone), bucket)
+
+ const points: UsageSeriesPoint[] = []
+ const cursor = civilDate(first)
+ let guard = 0
+
+ // `YYYY-MM-DD` sorts lexicographically in calendar order, so this compares dates.
+ while (civilKey(cursor) <= last && guard < 1000) {
+ guard += 1
+ const key = civilKey(cursor)
+ const row = byBucket.get(key)
+ points.push({
+ timestamp: `${key}T00:00:00`,
+ cost: toNumber(row?.cost),
+ events: Math.round(toNumber(row?.events)),
+ })
+ if (bucket === 'day') cursor.setUTCDate(cursor.getUTCDate() + 1)
+ else if (bucket === 'week') cursor.setUTCDate(cursor.getUTCDate() + 7)
+ else cursor.setUTCMonth(cursor.getUTCMonth() + 1)
+ }
+
+ return points
+}
+
+export interface UsageBreakdownEntry {
+ id: string
+ label: string
+ cost: number
+ events: number
+ /** Share of the window total, 0..1 — of everything, not just the visible rows. */
+ share: number
+}
+
+export interface UsageBreakdownFold {
+ rows: UsageBreakdownEntry[]
+ /** `tokens` is the omitted rows' total, so a token-denominated tab still adds up. */
+ other: { cost: number; events: number; rowCount: number; tokens: number }
+ totalCost: number
+}
+
+interface RankedRow {
+ key: string | null
+ cost: string | number | null
+ events: number | string | null
+ inputTokens?: number
+ outputTokens?: number
+}
+
+/**
+ * Ranks a dimension and closes it with an explicit remainder.
+ *
+ * The remainder is not cosmetic: five lists that do not add up to the headline
+ * number is the classic "the numbers are wrong" bug, and the only way a truncated
+ * ranking can reconcile is by naming what it left out.
+ *
+ * `rankBy` exists because BYOK has no cost to rank by — every row is zero by
+ * definition, so a cost sort fell through to its alphabetical tiebreak and the
+ * "top providers" were whichever ones sorted first. A tab denominated in tokens
+ * has to rank by tokens.
+ */
+export function foldUsageBreakdown(
+ rows: RankedRow[],
+ totalCost: number,
+ labelFor: (key: string | null) => string,
+ limit: number,
+ rankBy: 'cost' | 'tokens' = 'cost'
+): UsageBreakdownFold {
+ const ranked = rows
+ .map((row) => ({
+ id: row.key ?? '',
+ label: labelFor(row.key),
+ cost: toNumber(row.cost),
+ events: Math.round(toNumber(row.events)),
+ tokens: (row.inputTokens ?? 0) + (row.outputTokens ?? 0),
+ }))
+ .sort(
+ (left, right) =>
+ (rankBy === 'tokens' ? right.tokens - left.tokens : right.cost - left.cost) ||
+ left.label.localeCompare(right.label)
+ )
+
+ const visible = ranked.slice(0, limit)
+ const hidden = ranked.slice(limit)
+ /**
+ * Share is measured in whatever the list is ranked by, because it is what draws the
+ * bar. On BYOK every row costs zero, so a cost-based share made every provider's bar
+ * identical — the minimum width — and the ranking above became invisible.
+ */
+ const shareTotal =
+ rankBy === 'tokens' ? ranked.reduce((sum, row) => sum + row.tokens, 0) : totalCost
+ const share = (row: { cost: number; tokens: number }) =>
+ shareTotal > 0 ? (rankBy === 'tokens' ? row.tokens : row.cost) / shareTotal : 0
+
+ return {
+ rows: visible.map(({ tokens: _tokens, ...row }) => ({
+ ...row,
+ share: share({ cost: row.cost, tokens: _tokens }),
+ })),
+ other: {
+ cost: hidden.reduce((sum, row) => sum + row.cost, 0),
+ events: hidden.reduce((sum, row) => sum + row.events, 0),
+ rowCount: hidden.length,
+ // BYOK ranks by cost, which is zero for every row, so the visible slice is
+ // effectively arbitrary — omitting the tail's tokens would hide real volume.
+ tokens: hidden.reduce((sum, row) => sum + row.tokens, 0),
+ },
+ totalCost,
+ }
+}
+
+/**
+ * What a null grouping key means, per dimension.
+ *
+ * A single "Unattributed" label was wrong in both directions: on Workspaces it means
+ * usage that belongs to no workspace, and on Workflows it means usage that never came
+ * from a workflow — which is most of an organization's spend, and reading that as an
+ * attribution failure is what made the workflow list useless.
+ */
+export const USAGE_NULL_KEY_LABELS: Record = {
+ member: 'Unknown member',
+ workspace: 'No workspace',
+ // Unreachable: the workflow dimension filters null ids out entirely.
+ workflow: 'Unknown workflow',
+ model: 'Unknown model',
+ byok: 'Unknown provider',
+ source: 'Other',
+}
+
+export interface MergeableRow {
+ key: string | null
+ cost: string | number | null
+ events: number | string | null
+ inputTokens?: number
+ outputTokens?: number
+}
+
+/**
+ * Re-keys rows onto a coarser identity and sums the collisions.
+ *
+ * Needed wherever the SQL grouping column is finer than what the panel shows. The
+ * ledger stores `copilot` and `workspace-chat` as distinct sources but both display
+ * as "Sim Chat", so grouping by the raw column alone renders the same label twice
+ * with the usage split across the two rows — which reads as a bug and makes the
+ * ranking wrong. Models collapse to a provider the same way.
+ */
+export function mergeRowsByKey(
+ rows: T[],
+ resolveKey: (key: string | null) => string | null
+): MergeableRow[] {
+ const merged = new Map()
+ for (const row of rows) {
+ const key = resolveKey(row.key)
+ const mapKey = key ?? ''
+ const existing = merged.get(mapKey)
+ if (!existing) {
+ merged.set(mapKey, {
+ key,
+ cost: toNumber(row.cost),
+ events: Math.round(toNumber(row.events)),
+ ...(row.inputTokens !== undefined ? { inputTokens: row.inputTokens } : {}),
+ ...(row.outputTokens !== undefined ? { outputTokens: row.outputTokens } : {}),
+ })
+ continue
+ }
+ existing.cost = toNumber(existing.cost) + toNumber(row.cost)
+ existing.events = Math.round(toNumber(existing.events) + toNumber(row.events))
+ if (row.inputTokens !== undefined) {
+ existing.inputTokens = (existing.inputTokens ?? 0) + row.inputTokens
+ }
+ if (row.outputTokens !== undefined) {
+ existing.outputTokens = (existing.outputTokens ?? 0) + row.outputTokens
+ }
+ }
+ return [...merged.values()]
+}
diff --git a/apps/sim/lib/billing/core/usage-log.test.ts b/apps/sim/lib/billing/core/usage-log.test.ts
index 6568fd4dacc..1098fddc277 100644
--- a/apps/sim/lib/billing/core/usage-log.test.ts
+++ b/apps/sim/lib/billing/core/usage-log.test.ts
@@ -159,6 +159,53 @@ describe('recordUsage', () => {
expect(mockGetHighestPrioritySubscription).not.toHaveBeenCalled()
expect(mockInsert).not.toHaveBeenCalled()
})
+
+ it('keeps zero-cost unbilled rows and still drops every other zero-cost entry', async () => {
+ await recordUsage({
+ userId: 'user-1',
+ billingEntity: { type: 'organization', id: 'org-1' },
+ billingPeriod: {
+ start: new Date('2026-05-01T00:00:00.000Z'),
+ end: new Date('2026-06-01T00:00:00.000Z'),
+ },
+ executionId: 'execution-1',
+ entries: [
+ {
+ category: 'model_unbilled',
+ source: 'workflow',
+ description: 'claude-sonnet-4',
+ cost: 0,
+ metadata: { inputTokens: 1200, outputTokens: 340 },
+ },
+ // A billed category at zero cost is still noise, and stays filtered.
+ { category: 'model', source: 'workflow', description: 'gpt-4', cost: 0 },
+ { category: 'tool', source: 'workflow', description: 'exa_search', cost: 0 },
+ ],
+ })
+
+ const values = mockValues.mock.calls[0][0]
+ expect(values).toHaveLength(1)
+ expect(values[0]).toMatchObject({
+ category: 'model_unbilled',
+ cost: '0',
+ description: 'claude-sonnet-4',
+ metadata: { inputTokens: 1200, outputTokens: 340 },
+ })
+ })
+
+ it('writes nothing when every entry is zero-cost and billable', async () => {
+ await recordUsage({
+ userId: 'user-1',
+ billingEntity: { type: 'user', id: 'user-1' },
+ billingPeriod: {
+ start: new Date('2026-05-01T00:00:00.000Z'),
+ end: new Date('2026-06-01T00:00:00.000Z'),
+ },
+ entries: [{ category: 'model', source: 'workflow', description: 'gpt-4', cost: 0 }],
+ })
+
+ expect(mockInsert).not.toHaveBeenCalled()
+ })
})
describe('resolveCumulativeTopUp', () => {
diff --git a/apps/sim/lib/billing/core/usage-log.ts b/apps/sim/lib/billing/core/usage-log.ts
index 552f15b4e2f..ed0609b2e01 100644
--- a/apps/sim/lib/billing/core/usage-log.ts
+++ b/apps/sim/lib/billing/core/usage-log.ts
@@ -4,7 +4,7 @@ import { usageLog, workflow } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import { generateId } from '@sim/utils/id'
-import { and, desc, eq, gte, inArray, lt, lte, or, sql } from 'drizzle-orm'
+import { and, desc, eq, gte, inArray, lt, lte, notInArray, or, sql } from 'drizzle-orm'
import { defaultBillingPeriod } from '@/lib/billing/core/billing-period'
import { getHighestPrioritySubscription } from '@/lib/billing/core/plan'
import {
@@ -21,9 +21,12 @@ import type { DbClient, DbOrTx } from '@/lib/db/types'
const logger = createLogger('UsageLog')
/**
- * Usage log category types
+ * Usage log category types.
+ *
+ * `model_unbilled` is reporting-only — see {@link UNBILLED_USAGE_CATEGORIES}. Code
+ * that means "a charge" must not treat it as one.
*/
-export type UsageLogCategory = 'model' | 'fixed' | 'tool'
+export type UsageLogCategory = 'model' | 'fixed' | 'tool' | 'model_unbilled'
/**
* Usage log source types
@@ -42,6 +45,26 @@ export const COPILOT_USAGE_SOURCES: UsageLogSource[] = [
'mothership_block',
]
+/**
+ * Categories that record usage Sim does not charge for. Their `cost` is always `0`
+ * and their value is the token counts in `metadata`, so usage reporting can show
+ * volume that the billing ledger has no reason to know about.
+ *
+ * These are the only categories exempt from {@link recordUsage}'s `cost > 0` filter.
+ * Every billing aggregate over usage_log is `SUM(cost)`, so zero-cost rows leave
+ * every existing total unchanged.
+ */
+export const UNBILLED_USAGE_CATEGORIES = [
+ 'model_unbilled',
+] as const satisfies readonly UsageLogCategory[]
+
+const UNBILLED_USAGE_CATEGORY_SET: ReadonlySet = new Set(UNBILLED_USAGE_CATEGORIES)
+
+/** True for a category whose rows are recorded for reporting rather than billing. */
+export function isUnbilledUsageCategory(category: UsageLogCategory): boolean {
+ return UNBILLED_USAGE_CATEGORY_SET.has(category)
+}
+
/**
* Metadata for 'model' category charges
*/
@@ -219,6 +242,12 @@ export async function getBillingPeriodUsageCost(
* Counts distinct workflow executions that produced billable ledger entries in
* an attributed billing period. Multiple line items for one execution count as
* one run; executions with no billable usage are intentionally excluded.
+ *
+ * The category filter is what keeps that last clause true now that unbilled rows
+ * exist. A BYOK-only run whose base charge is zero writes nothing but a
+ * `model_unbilled` row, and without this it would newly appear in a count that
+ * feeds the enterprise billing preview — a customer-facing number moving because
+ * of a reporting-only row.
*/
export async function getBillingPeriodWorkflowRunCount(
billingEntity: BillingEntity,
@@ -227,8 +256,15 @@ export async function getBillingPeriodWorkflowRunCount(
): Promise {
const [row] = await executor
.select({
+ /**
+ * The exclusion goes through `notInArray`, not `<> ALL(${array})`. Interpolating
+ * a JavaScript array into a `sql` template emits parenthesized scalar binds —
+ * `ALL(($1))` — which Postgres rejects outright with "op ANY/ALL (array)
+ * requires array on right side". Unit tests cannot catch it, because `@sim/db`
+ * is mocked and no statement is ever rendered.
+ */
workflowRuns:
- sql`COUNT(DISTINCT ${usageLog.executionId}) FILTER (WHERE ${usageLog.source} = 'workflow')`.mapWith(
+ sql`COUNT(DISTINCT ${usageLog.executionId}) FILTER (WHERE ${usageLog.source} = 'workflow' AND ${notInArray(usageLog.category, [...UNBILLED_USAGE_CATEGORIES])})`.mapWith(
Number
),
})
@@ -397,7 +433,12 @@ export async function recordUsage(params: RecordUsageParams): Promise {
tx,
} = params
- const validEntries = entries.filter((e) => e.cost > 0)
+ // An unbilled row is admitted only at exactly zero cost. The category's whole
+ // safety argument is that every billing aggregate is `SUM(cost)` and these rows
+ // contribute nothing; a nonzero one would quietly move real money.
+ const validEntries = entries.filter((e) =>
+ isUnbilledUsageCategory(e.category) ? e.cost === 0 : e.cost > 0
+ )
if (validEntries.length === 0) {
return
@@ -721,17 +762,52 @@ interface UsageLogFilter {
source?: UsageLogSource | UsageLogSource[]
workspaceId?: string
startDate?: Date
+ /**
+ * Inclusive by default, which is what the personal credit-usage surfaces have
+ * always meant by it.
+ */
endDate?: Date
+ /**
+ * Treat {@link endDate} as exclusive instead.
+ *
+ * Analytics windows are half-open `[start, end)` — a billing period's end
+ * instant is the next period's start — so a row stamped exactly on the
+ * boundary belongs to the next window. Without this the event list and the
+ * export counted it while the summary and breakdowns did not, and the two
+ * disagreed by one row at exactly the moment a period rolls over.
+ */
+ endDateExclusive?: boolean
+ /**
+ * Match the stamped billing period instead of a `created_at` range.
+ *
+ * A stripe or default period is what rows are *stamped* with, and that is the
+ * predicate every analytics read uses for it. Filtering the same window on
+ * `created_at` selects a different set — a row created inside the period but
+ * stamped to another, or the reverse — so the event list and the CSV covered
+ * different rows than the totals above them. Mutually exclusive with
+ * {@link startDate}/{@link endDate}; a reporting period and a plain range still
+ * use those, exactly as `buildUsageAnalyticsScope` does.
+ */
+ billingPeriod?: { start: Date; end: Date }
}
-type UsageLogScope = { kind: 'user'; userId: string } | { kind: 'workspace'; workspaceId: string }
+type UsageLogScope =
+ | { kind: 'user'; userId: string }
+ | { kind: 'workspace'; workspaceId: string }
+ /** Every event billed to a payer, regardless of which actor or workspace produced it. */
+ | { kind: 'billingEntity'; entity: BillingEntity }
+
+function scopeCondition(scope: UsageLogScope) {
+ if (scope.kind === 'user') return eq(usageLog.userId, scope.userId)
+ if (scope.kind === 'workspace') return eq(usageLog.workspaceId, scope.workspaceId)
+ return and(
+ eq(usageLog.billingEntityType, scope.entity.type),
+ eq(usageLog.billingEntityId, scope.entity.id)
+ )
+}
function buildUsageLogConditions(scope: UsageLogScope, filter: UsageLogFilter) {
- const conditions = [
- scope.kind === 'user'
- ? eq(usageLog.userId, scope.userId)
- : eq(usageLog.workspaceId, scope.workspaceId),
- ]
+ const conditions = [scopeCondition(scope)]
if (filter.source) {
conditions.push(
Array.isArray(filter.source)
@@ -740,8 +816,21 @@ function buildUsageLogConditions(scope: UsageLogScope, filter: UsageLogFilter) {
)
}
if (filter.workspaceId) conditions.push(eq(usageLog.workspaceId, filter.workspaceId))
+ if (filter.billingPeriod) {
+ conditions.push(
+ eq(usageLog.billingPeriodStart, filter.billingPeriod.start),
+ eq(usageLog.billingPeriodEnd, filter.billingPeriod.end)
+ )
+ return conditions
+ }
if (filter.startDate) conditions.push(gte(usageLog.createdAt, filter.startDate))
- if (filter.endDate) conditions.push(lte(usageLog.createdAt, filter.endDate))
+ if (filter.endDate) {
+ conditions.push(
+ filter.endDateExclusive
+ ? lt(usageLog.createdAt, filter.endDate)
+ : lte(usageLog.createdAt, filter.endDate)
+ )
+ }
return conditions
}
@@ -823,8 +912,19 @@ export interface GetUsageLogsOptions {
workspaceId?: string
/** Start date (inclusive) */
startDate?: Date
- /** End date (inclusive) */
+ /** End date (inclusive, unless {@link endDateExclusive}) */
endDate?: Date
+ /**
+ * Treat {@link endDate} as exclusive, matching a half-open analytics window.
+ * See {@link UsageLogFilter.endDateExclusive}.
+ */
+ endDateExclusive?: boolean
+ /**
+ * Match the stamped billing period instead of a `created_at` range, so a
+ * ledger listing covers the same rows an analytics read of the same window
+ * does. See {@link UsageLogFilter.billingPeriod}.
+ */
+ billingPeriod?: { start: Date; end: Date }
/** Maximum number of results */
limit?: number
/** Cursor for pagination (log ID) */
@@ -891,6 +991,8 @@ async function getUsageLogs(
workspaceId,
startDate,
endDate,
+ endDateExclusive,
+ billingPeriod,
limit = 50,
cursor,
cursorCreatedAt,
@@ -898,7 +1000,14 @@ async function getUsageLogs(
} = options
try {
- const conditions = buildUsageLogConditions(scope, { source, workspaceId, startDate, endDate })
+ const conditions = buildUsageLogConditions(scope, {
+ source,
+ workspaceId,
+ startDate,
+ endDate,
+ endDateExclusive,
+ billingPeriod,
+ })
if (cursor) {
let resolvedCursorCreatedAt = cursorCreatedAt
@@ -972,6 +1081,8 @@ async function getUsageLogs(
workspaceId,
startDate,
endDate,
+ endDateExclusive,
+ billingPeriod,
})
const summaryResult = await dbReplica
@@ -1029,6 +1140,20 @@ export function getUserUsageLogs(
return getUsageLogs({ kind: 'user', userId }, options)
}
+/**
+ * Gets every usage event billed to a payer, regardless of actor or workspace.
+ *
+ * This is the organization-wide ledger the usage panel pages through. It reuses this
+ * module's keyset pagination, cursor handling, and workflow-name join rather than
+ * reimplementing them — the only thing it adds is the scope predicate.
+ */
+export function getBillingEntityUsageLogs(
+ entity: BillingEntity,
+ options: GetUsageLogsOptions = {}
+): Promise {
+ return getUsageLogs({ kind: 'billingEntity', entity }, options)
+}
+
/** Gets usage logs attributed to the selected workspace, regardless of actor. */
export function getWorkspaceUsageLogs(
workspaceId: string,
diff --git a/apps/sim/lib/billing/core/usage.ts b/apps/sim/lib/billing/core/usage.ts
index b9ec280ec81..b0c5045af01 100644
--- a/apps/sim/lib/billing/core/usage.ts
+++ b/apps/sim/lib/billing/core/usage.ts
@@ -708,9 +708,15 @@ export async function maybeSendUsageThresholdEmail(params: {
const upgradeCreditsLink = params.workspaceId
? `${baseUrl}${buildUpgradeHref(params.workspaceId, 'credits')}`
: `${baseUrl}/workspace`
+ /**
+ * Organization billing is reached through the workspace the usage occurred in
+ * — that is the only plane that serves it. Without a workspace there is no such
+ * link to build, so the account page is the honest fallback rather than a guess
+ * at which workspace the recipient would want.
+ */
const billingSettingsLink =
- params.scope === 'organization' && params.organizationId
- ? `${baseUrl}/organization/${params.organizationId}/settings/billing`
+ params.scope === 'organization' && params.workspaceId
+ ? `${baseUrl}/workspace/${params.workspaceId}/settings/billing`
: `${baseUrl}/account/settings/billing`
// Check for 80% threshold crossing — used for paid users (budget warning) and free users (upgrade nudge)
diff --git a/apps/sim/lib/billing/usage-sources.ts b/apps/sim/lib/billing/usage-sources.ts
index f74bf99730a..056285e0a30 100644
--- a/apps/sim/lib/billing/usage-sources.ts
+++ b/apps/sim/lib/billing/usage-sources.ts
@@ -61,6 +61,15 @@ const BILLING_TO_INTERNAL_SOURCES = {
'voice-output': ['voice-output'],
} as const satisfies Record
+/**
+ * What each source is called wherever usage is shown.
+ *
+ * `workflow` covers everything one run consumed — the models it called on Sim's
+ * hosted keys, any hosted-key tool calls, and the per-run execution fee — so the
+ * label stays the broad "Workflow". Naming it after the execution fee would
+ * understate it by orders of magnitude; the fee is a rounding error beside the
+ * model cost.
+ */
export const BILLING_USAGE_LOG_SOURCE_LABELS = {
workflow: 'Workflow',
wand: 'Wand',
diff --git a/apps/sim/lib/copilot/generated/docs-manifest.ts b/apps/sim/lib/copilot/generated/docs-manifest.ts
index dfad9b4f730..39e1656d64d 100644
--- a/apps/sim/lib/copilot/generated/docs-manifest.ts
+++ b/apps/sim/lib/copilot/generated/docs-manifest.ts
@@ -369,6 +369,7 @@ export const DOCS_MANIFEST: readonly string[] = [
'platform/enterprise/self-hosted.mdx',
'platform/enterprise/session-policies.mdx',
'platform/enterprise/sso.mdx',
+ 'platform/enterprise/usage-tracking.mdx',
'platform/enterprise/verified-domains.mdx',
'platform/enterprise/whitelabeling.mdx',
'platform/organization.mdx',
diff --git a/apps/sim/lib/core/config/enterprise-entitlements.ts b/apps/sim/lib/core/config/enterprise-entitlements.ts
index 3c30d68b0fa..f48fd8ca9a0 100644
--- a/apps/sim/lib/core/config/enterprise-entitlements.ts
+++ b/apps/sim/lib/core/config/enterprise-entitlements.ts
@@ -37,6 +37,7 @@ export type EnterpriseFeature =
| 'sandboxes'
| 'sessionPolicies'
| 'sso'
+ | 'usageMonitoring'
| 'whitelabeling'
/**
@@ -59,6 +60,9 @@ export type EnterpriseFeature =
* was always writable when billing was off and stays that way; only the
* delete pass is gated here. Defaulting it on would start expiring logs on
* upgrade against plan defaults the operator never chose.
+ * - `usageMonitoring` had no prior behavior at all — it ships with this flag —
+ * and it discloses every member's spend, so it stays opt-in rather than
+ * appearing unannounced on upgrade.
*
* `sandboxes` is deliberately `false`. A remote Function provider and immutable
* base are operational prerequisites, so a billing-free deployment must opt in
@@ -80,6 +84,7 @@ export const ENTERPRISE_FEATURE_LEGACY_DEFAULTS: Readonly {
const rows = await db
@@ -26,7 +31,13 @@ export async function buildCostLedger(executionId: string): Promise {
const statsLog = logger.withMetadata({ workflowId: workflowId ?? undefined, executionId })
@@ -1570,7 +1578,18 @@ export class ExecutionLogger implements IExecutionLoggerService {
target: number
metadata?: ModelUsageMetadata | null
}
+ /**
+ * Model usage Sim does not charge for — a call funded by the customer's own
+ * provider key. `notBilledCost()` zeroes the cost but leaves the span's token
+ * counts intact, so these carry real volume with `cost: 0`. Recorded for the
+ * organization usage panel; they are never a charge and never a delta.
+ */
+ type UnbilledLine = {
+ description: string
+ metadata: ModelUsageMetadata
+ }
const targets: TargetLine[] = []
+ const unbilledLines: UnbilledLine[] = []
const workflowLedgerModels = costSummary.workflowLedgerModels ?? costSummary.models ?? {}
const totalModelCost = Object.values(costSummary.models ?? {}).reduce(
(sum, model) => sum + model.total,
@@ -1603,6 +1622,14 @@ export class ExecutionLogger implements IExecutionLoggerService {
modelData.toolCost > 0 && { toolCost: modelData.toolCost }),
},
})
+ } else if (modelData.tokens.input > 0 || modelData.tokens.output > 0) {
+ unbilledLines.push({
+ description: modelName,
+ metadata: {
+ inputTokens: modelData.tokens.input,
+ outputTokens: modelData.tokens.output,
+ },
+ })
}
}
@@ -1620,11 +1647,22 @@ export class ExecutionLogger implements IExecutionLoggerService {
}
}
+ // Unbilled rows are reporting-only, so they must never be the reason a run
+ // demands billing attribution it does not have: a BYOK-only run without
+ // attribution has to bail exactly as it did before unbilled capture existed,
+ // or it starts throwing where it previously succeeded. Terminal-only because
+ // these lines carry no cost delta to reconcile — they are written once, with
+ // the run's cumulative tokens.
+ const canRecordUnbilled =
+ isTerminalBoundary &&
+ unbilledLines.length > 0 &&
+ (!workflowRecord.workspaceId || Boolean(billingContext))
+
// Bail before requiring billing attribution: a run with no billable target
// (e.g. a preprocessing-gated run that never executed) writes no ledger row
// either way, so demanding attribution here would raise a lost-revenue
// error for a charge that does not exist.
- if (targets.length === 0) {
+ if (targets.length === 0 && !canRecordUnbilled) {
statsLog.debug('No cost to record')
return 0
}
@@ -1682,6 +1720,29 @@ export class ExecutionLogger implements IExecutionLoggerService {
return entries
}
+ /**
+ * Zero-cost lines for BYOK models not already recorded for this execution.
+ * Unlike the cost path, *presence* — not amount — is the idempotency signal,
+ * because these lines never change value once written. The `eventKey` +
+ * `onConflictDoNothing` is the real guard; this filter only avoids a
+ * pointless insert on a retried terminal boundary.
+ */
+ const buildUnbilledEntries = (recordedKeys: ReadonlySet) =>
+ unbilledLines
+ .filter((line) => !recordedKeys.has(`model_unbilled::${line.description}`))
+ .map((line) => ({
+ category: 'model_unbilled' as const,
+ source: 'workflow' as const,
+ description: line.description,
+ cost: 0,
+ eventKey: stableEventKey({
+ executionId: executionId ?? '',
+ category: 'model_unbilled',
+ description: line.description,
+ }),
+ metadata: line.metadata,
+ }))
+
if (executionId) {
// Serialize concurrent completion boundaries for this execution so the
// read-then-insert reconciliation cannot race. pg_advisory_xact_lock is
@@ -1711,18 +1772,20 @@ export class ExecutionLogger implements IExecutionLoggerService {
.groupBy(usageLog.category, usageLog.description)
const alreadyBilled = new Map()
+ const recordedKeys = new Set()
for (const row of billedRows) {
- alreadyBilled.set(
- `${row.category}::${row.description}`,
- Number.parseFloat(row.cost ?? '0')
- )
+ const key = `${row.category}::${row.description}`
+ alreadyBilled.set(key, Number.parseFloat(row.cost ?? '0'))
+ recordedKeys.add(key)
}
const entries = buildDeltaEntries(alreadyBilled)
- if (entries.length > 0) {
+ const unbilledEntries = canRecordUnbilled ? buildUnbilledEntries(recordedKeys) : []
+ const allEntries = [...entries, ...unbilledEntries]
+ if (allEntries.length > 0) {
await recordUsage({
userId,
- entries,
+ entries: allEntries,
workspaceId: workflowRecord.workspaceId ?? undefined,
workflowId,
executionId,
@@ -1730,6 +1793,8 @@ export class ExecutionLogger implements IExecutionLoggerService {
billingEntity: resolvedBillingContext.billingEntity,
billingPeriod: resolvedBillingContext.billingPeriod,
})
+ // Billable deltas only: unbilled lines cost 0, and the caller drives
+ // usage-threshold math off this number.
recordedIncrement = entries.reduce((acc, e) => acc + e.cost, 0)
// Refine cost_total to the EXACT post-reconciliation ledger sum,
@@ -1739,24 +1804,33 @@ export class ExecutionLogger implements IExecutionLoggerService {
// the prior workflow-source sum plus the deltas just inserted. This
// supersedes the main-transaction GREATEST baseline except when the
// display total contains Mothership cost owned by Go update-cost.
- const ledgerSum =
- [...alreadyBilled.values()].reduce((acc, v) => acc + v, 0) + recordedIncrement
- const displayedCostTotal =
- externallyLedgeredModelCost > 0 ? costSummary.totalCost : ledgerSum
- await tx
- .update(workflowExecutionLogs)
- .set({ costTotal: displayedCostTotal.toString() })
- .where(eq(workflowExecutionLogs.executionId, executionId))
+ //
+ // Gated on billable deltas: a boundary that only wrote zero-cost
+ // unbilled rows has changed no cost, and must not restate cost_total.
+ if (entries.length > 0) {
+ const ledgerSum =
+ [...alreadyBilled.values()].reduce((acc, v) => acc + v, 0) + recordedIncrement
+ const displayedCostTotal =
+ externallyLedgeredModelCost > 0 ? costSummary.totalCost : ledgerSum
+ await tx
+ .update(workflowExecutionLogs)
+ .set({ costTotal: displayedCostTotal.toString() })
+ .where(eq(workflowExecutionLogs.executionId, executionId))
+ }
}
})
} else {
// No execution scope to reconcile/lock against (not expected at a
// workflow completion): record the full targets directly.
const entries = buildDeltaEntries(new Map())
- if (entries.length > 0) {
+ const allEntries = [
+ ...entries,
+ ...(canRecordUnbilled ? buildUnbilledEntries(new Set()) : []),
+ ]
+ if (allEntries.length > 0) {
await recordUsage({
userId,
- entries,
+ entries: allEntries,
workspaceId: workflowRecord.workspaceId ?? undefined,
workflowId,
billingEntity: resolvedBillingContext.billingEntity,
diff --git a/apps/sim/lib/logs/execution/logging-factory.test.ts b/apps/sim/lib/logs/execution/logging-factory.test.ts
index 7dabdc6955b..45477d09a4c 100644
--- a/apps/sim/lib/logs/execution/logging-factory.test.ts
+++ b/apps/sim/lib/logs/execution/logging-factory.test.ts
@@ -197,6 +197,34 @@ describe('calculateCostSummary', () => {
expect(result.models['gpt-4'].total).toBe(0.03)
})
+ test('keeps tokens for a zero-cost BYOK span so unbilled usage stays reportable', () => {
+ // A BYOK model resolves through notBilledCost(), which zeroes every cost field
+ // but leaves `cost` DEFINED and the token counts intact. hasBillableCost() tests
+ // `cost !== undefined`, not `cost.total > 0`, which is the only reason this span
+ // reaches the summary at all. Narrowing that predicate would silently stop the
+ // organization usage panel from ever seeing BYOK volume.
+ const traceSpans = [
+ {
+ id: 'span-1',
+ name: 'Agent Block',
+ type: 'agent',
+ model: 'claude-sonnet-4',
+ cost: { input: 0, output: 0, total: 0 },
+ tokens: { input: 1200, output: 340, total: 1540 },
+ },
+ ]
+
+ const result = calculateCostSummary(traceSpans)
+
+ expect(result.totalCost).toBe(BASE_EXECUTION_CHARGE)
+ expect(result.workflowLedgerModels['claude-sonnet-4']).toMatchObject({
+ total: 0,
+ tokens: { input: 1200, output: 340, total: 1540 },
+ })
+ expect(result.totalPromptTokens).toBe(1200)
+ expect(result.totalCompletionTokens).toBe(340)
+ })
+
test('should calculate cost from multiple spans', () => {
const traceSpans = [
{
diff --git a/apps/sim/lib/logs/stats-logs.ts b/apps/sim/lib/logs/stats-logs.ts
index 6a8abc1c6ab..6d98e2a5174 100644
--- a/apps/sim/lib/logs/stats-logs.ts
+++ b/apps/sim/lib/logs/stats-logs.ts
@@ -1,6 +1,7 @@
import { dbReplica } from '@sim/db'
import { workflow, workflowExecutionLogs } from '@sim/db/schema'
import { and, eq, sql } from 'drizzle-orm'
+import { assertValidTimezone } from '@/lib/core/utils/timezone'
import { buildFilterConditions } from '@/lib/logs/filters'
import { expandFolderIdsWithDescendants } from '@/lib/logs/folder-expansion'
import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
@@ -58,14 +59,6 @@ export interface LogStatsResponse {
const MAX_WORKFLOWS = 100
-function assertValidTimezone(timezone: string): void {
- try {
- new Intl.DateTimeFormat('en-US', { timeZone: timezone })
- } catch {
- throw new Error(`Invalid timezone: ${timezone}. Use an IANA name like "America/Los_Angeles".`)
- }
-}
-
interface StatsAccumulator {
executions: number
byStatus: Record
diff --git a/packages/db/migrations/0311_usage_log_model_unbilled_category.sql b/packages/db/migrations/0311_usage_log_model_unbilled_category.sql
new file mode 100644
index 00000000000..a3512d14b73
--- /dev/null
+++ b/packages/db/migrations/0311_usage_log_model_unbilled_category.sql
@@ -0,0 +1,7 @@
+-- Adds the `model_unbilled` usage_log category so BYOK model usage can be recorded at
+-- zero cost for reporting. Purely additive: no existing row changes category, and every
+-- billing aggregate over usage_log is SUM(cost), which zero-cost rows leave untouched.
+--
+-- Postgres cannot use a new enum value in the same transaction that adds it, so this
+-- must be released BEFORE any code writes it.
+ALTER TYPE "public"."usage_log_category" ADD VALUE IF NOT EXISTS 'model_unbilled';
diff --git a/packages/db/migrations/meta/0311_snapshot.json b/packages/db/migrations/meta/0311_snapshot.json
new file mode 100644
index 00000000000..8f2d20e9b7e
--- /dev/null
+++ b/packages/db/migrations/meta/0311_snapshot.json
@@ -0,0 +1,20297 @@
+{
+ "id": "5f0b0b6d-ba9e-470e-92e8-7e77b078ba93",
+ "prevId": "32457215-9732-42d6-82e2-ec2d5ff12485",
+ "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
+ },
+ "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.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
+ },
+ "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
+ },
+ "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
+ },
+ "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
+ },
+ "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_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"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "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_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
+ },
+ "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
+ },
+ "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_execution_id_idx": {
+ "name": "copilot_runs_execution_id_idx",
+ "columns": [
+ {
+ "expression": "execution_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "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_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"
+ }
+ },
+ "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": true
+ },
+ "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
+ },
+ "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
+ },
+ "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
+ },
+ "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": 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": {
+ "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_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_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": {}
+ }
+ },
+ "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_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_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_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 cardinality(granted_scopes) > 0\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_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": true
+ },
+ "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_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_status_idx": {
+ "name": "credential_group_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": {}
+ },
+ "credential_group_workspace_name_unique": {
+ "name": "credential_group_workspace_name_unique",
+ "columns": [
+ {
+ "expression": "workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "lower(\"name\")",
+ "asc": true,
+ "isExpression": 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_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": {},
+ "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
+ },
+ "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_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_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.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
+ },
+ "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
+ },
+ "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_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_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_id_idx": {
+ "name": "doc_connector_id_idx",
+ "columns": [
+ {
+ "expression": "connector_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "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_tag1_idx": {
+ "name": "doc_tag1_idx",
+ "columns": [
+ {
+ "expression": "tag1",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "doc_tag2_idx": {
+ "name": "doc_tag2_idx",
+ "columns": [
+ {
+ "expression": "tag2",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "doc_tag3_idx": {
+ "name": "doc_tag3_idx",
+ "columns": [
+ {
+ "expression": "tag3",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "doc_tag4_idx": {
+ "name": "doc_tag4_idx",
+ "columns": [
+ {
+ "expression": "tag4",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "doc_tag5_idx": {
+ "name": "doc_tag5_idx",
+ "columns": [
+ {
+ "expression": "tag5",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "doc_tag6_idx": {
+ "name": "doc_tag6_idx",
+ "columns": [
+ {
+ "expression": "tag6",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "doc_tag7_idx": {
+ "name": "doc_tag7_idx",
+ "columns": [
+ {
+ "expression": "tag7",
+ "isExpression": false,
+ "asc": 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": false,
+ "method": "btree",
+ "with": {}
+ },
+ "doc_date2_idx": {
+ "name": "doc_date2_idx",
+ "columns": [
+ {
+ "expression": "date2",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "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": {},
+ "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_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_kb_id_idx": {
+ "name": "emb_kb_id_idx",
+ "columns": [
+ {
+ "expression": "knowledge_base_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "emb_doc_id_idx": {
+ "name": "emb_doc_id_idx",
+ "columns": [
+ {
+ "expression": "document_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "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": {}
+ },
+ "embedding_vector_hnsw_idx": {
+ "name": "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
+ }
+ },
+ "emb_tag1_idx": {
+ "name": "emb_tag1_idx",
+ "columns": [
+ {
+ "expression": "tag1",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "emb_tag2_idx": {
+ "name": "emb_tag2_idx",
+ "columns": [
+ {
+ "expression": "tag2",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "emb_tag3_idx": {
+ "name": "emb_tag3_idx",
+ "columns": [
+ {
+ "expression": "tag3",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "emb_tag4_idx": {
+ "name": "emb_tag4_idx",
+ "columns": [
+ {
+ "expression": "tag4",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "emb_tag5_idx": {
+ "name": "emb_tag5_idx",
+ "columns": [
+ {
+ "expression": "tag5",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "emb_tag6_idx": {
+ "name": "emb_tag6_idx",
+ "columns": [
+ {
+ "expression": "tag6",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "emb_tag7_idx": {
+ "name": "emb_tag7_idx",
+ "columns": [
+ {
+ "expression": "tag7",
+ "isExpression": false,
+ "asc": 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": false,
+ "method": "btree",
+ "with": {}
+ },
+ "emb_date2_idx": {
+ "name": "emb_date2_idx",
+ "columns": [
+ {
+ "expression": "date2",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "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_not_null_check": {
+ "name": "embedding_not_null_check",
+ "value": "\"embedding\" IS NOT NULL"
+ }
+ },
+ "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
+ },
+ "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
+ },
+ "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_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": {}
+ }
+ },
+ "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_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": {},
+ "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
+ },
+ "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
+ },
+ "next_sync_at": {
+ "name": "next_sync_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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
+ }
+ },
+ "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": {}
+ }
+ },
+ "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"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "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
+ },
+ "error_message": {
+ "name": "error_message",
+ "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": {},
+ "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": true
+ },
+ "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"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "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": true
+ },
+ "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_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_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_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": {},
+ "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
+ },
+ "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_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_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.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
+ },
+ "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"
+ },
+ "departed_member_usage": {
+ "name": "departed_member_usage",
+ "type": "numeric",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "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_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.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_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": true
+ },
+ "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
+ },
+ "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_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_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": {},
+ "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
+ }
+ },
+ "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.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
+ },
+ "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": true
+ },
+ "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_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_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": {},
+ "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.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.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
+ },
+ "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
+ }
+ },
+ "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
+ },
+ "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"
+ }
+ },
+ "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
+ },
+ "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": {}
+ }
+ },
+ "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"
+ }
+ },
+ "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_workspace_id_idx": {
+ "name": "usage_log_workspace_id_idx",
+ "columns": [
+ {
+ "expression": "workspace_id",
+ "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
+ }
+ },
+ "indexes": {},
+ "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
+ },
+ "total_manual_executions": {
+ "name": "total_manual_executions",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_api_calls": {
+ "name": "total_api_calls",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_webhook_triggers": {
+ "name": "total_webhook_triggers",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_scheduled_executions": {
+ "name": "total_scheduled_executions",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_chat_executions": {
+ "name": "total_chat_executions",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_mcp_executions": {
+ "name": "total_mcp_executions",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_tokens_used": {
+ "name": "total_tokens_used",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_cost": {
+ "name": "total_cost",
+ "type": "numeric",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "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()"
+ },
+ "current_period_cost": {
+ "name": "current_period_cost",
+ "type": "numeric",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "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'"
+ },
+ "pro_period_cost_snapshot": {
+ "name": "pro_period_cost_snapshot",
+ "type": "numeric",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'0'"
+ },
+ "pro_period_cost_snapshot_at": {
+ "name": "pro_period_cost_snapshot_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "credit_balance": {
+ "name": "credit_balance",
+ "type": "numeric",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "total_copilot_cost": {
+ "name": "total_copilot_cost",
+ "type": "numeric",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "current_period_copilot_cost": {
+ "name": "current_period_copilot_cost",
+ "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'"
+ },
+ "total_copilot_tokens": {
+ "name": "total_copilot_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_copilot_calls": {
+ "name": "total_copilot_calls",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_mcp_copilot_calls": {
+ "name": "total_mcp_copilot_calls",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_mcp_copilot_cost": {
+ "name": "total_mcp_copilot_cost",
+ "type": "numeric",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "current_period_mcp_copilot_cost": {
+ "name": "current_period_mcp_copilot_cost",
+ "type": "numeric",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "storage_used_bytes": {
+ "name": "storage_used_bytes",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_active": {
+ "name": "last_active",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "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_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": {
+ "name": "cost",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "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_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
+ },
+ "created_at": {
+ "name": "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_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_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
+ },
+ "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": {
+ "name": "size",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "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": "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_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_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_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": {},
+ "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_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
+ }
+ },
+ "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", "env_workspace", "env_personal", "service_account"]
+ },
+ "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",
+ "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"
+ ]
+ },
+ "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",
+ "mcp_server",
+ "workflow_mcp_server",
+ "custom_block",
+ "custom_tool",
+ "skill"
+ ]
+ },
+ "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 f4da810976f..8e7df021eb3 100644
--- a/packages/db/migrations/meta/_journal.json
+++ b/packages/db/migrations/meta/_journal.json
@@ -2171,6 +2171,13 @@
"when": 1787878957459,
"tag": "0310_drop_orphaned_table_import_columns",
"breakpoints": true
+ },
+ {
+ "idx": 311,
+ "version": "7",
+ "when": 1787880668706,
+ "tag": "0311_usage_log_model_unbilled_category",
+ "breakpoints": true
}
]
}
diff --git a/packages/db/schema.ts b/packages/db/schema.ts
index d146e978478..d9b945958d1 100644
--- a/packages/db/schema.ts
+++ b/packages/db/schema.ts
@@ -3871,7 +3871,22 @@ export const auditLog = pgTable(
})
)
-export const usageLogCategoryEnum = pgEnum('usage_log_category', ['model', 'fixed', 'tool'])
+/**
+ * `model_unbilled` records model usage Sim does not charge for — a call funded by
+ * the customer's own provider key (BYOK). Its `cost` is always `0` and its value is
+ * the token counts in `metadata`, so the org usage panel can report volume the
+ * billing ledger has no reason to know about.
+ *
+ * It is deliberately a distinct category rather than a `category = 'model'` row with
+ * `cost = 0`: every existing and future `where category = 'model'` billing query
+ * stays blind to these rows unless it opts in.
+ */
+export const usageLogCategoryEnum = pgEnum('usage_log_category', [
+ 'model',
+ 'fixed',
+ 'tool',
+ 'model_unbilled',
+])
export const usageLogSourceEnum = pgEnum('usage_log_source', [
'workflow',
'wand',
diff --git a/packages/emcn/src/icons/chart-column.tsx b/packages/emcn/src/icons/chart-column.tsx
new file mode 100644
index 00000000000..4f701a0805e
--- /dev/null
+++ b/packages/emcn/src/icons/chart-column.tsx
@@ -0,0 +1,28 @@
+import type { SVGProps } from 'react'
+
+/**
+ * ChartColumn icon component - an axis with two columns, for usage and analytics
+ * surfaces
+ * @param props - SVG properties including className, fill, etc.
+ */
+export function ChartColumn(props: SVGProps) {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/packages/emcn/src/icons/index.ts b/packages/emcn/src/icons/index.ts
index 35fb8f96792..b849073258b 100644
--- a/packages/emcn/src/icons/index.ts
+++ b/packages/emcn/src/icons/index.ts
@@ -21,6 +21,7 @@ export { Bug } from './bug'
export { Building } from './building'
export { Calendar } from './calendar'
export { Camera } from './camera'
+export { ChartColumn } from './chart-column'
export { Check } from './check'
export { ChevronDown } from './chevron-down'
export { ChevronLeft } from './chevron-left'
diff --git a/packages/sim-setup/src/twins.ts b/packages/sim-setup/src/twins.ts
index 879fb49cc2e..1e69be103a8 100644
--- a/packages/sim-setup/src/twins.ts
+++ b/packages/sim-setup/src/twins.ts
@@ -21,6 +21,7 @@ export const FLAG_TWINS: ReadonlyArray<{ server: string; client: string }> = [
{ server: 'DISABLE_INVITATIONS', client: 'NEXT_PUBLIC_DISABLE_INVITATIONS' },
{ server: 'DISABLE_PUBLIC_API', client: 'NEXT_PUBLIC_DISABLE_PUBLIC_API' },
{ server: 'SSO_ENABLED', client: 'NEXT_PUBLIC_SSO_ENABLED' },
+ { server: 'USAGE_MONITORING_ENABLED', client: 'NEXT_PUBLIC_USAGE_MONITORING_ENABLED' },
{ server: 'EMAIL_PASSWORD_SIGNUP_ENABLED', client: 'NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED' },
{ server: 'E2B_ENABLED', client: 'NEXT_PUBLIC_E2B_ENABLED' },
{ server: 'SLACK_EXTENDED_SCOPES', client: 'NEXT_PUBLIC_SLACK_EXTENDED_SCOPES' },
@@ -46,6 +47,11 @@ export const SELF_HOST_UNLOCKS: ReadonlyArray<{ server: string; label: string; h
{ server: 'DATA_DRAINS_ENABLED', label: 'Data drains', hint: 'export streams' },
{ server: 'FORKING_ENABLED', label: 'Workflow forking', hint: '' },
{ server: 'INBOX_ENABLED', label: 'Inbox', hint: '' },
+ {
+ server: 'USAGE_MONITORING_ENABLED',
+ label: 'Usage monitoring',
+ hint: 'org-wide credit usage',
+ },
{ server: 'WHITELABELING_ENABLED', label: 'Whitelabeling', hint: 'custom branding' },
]
diff --git a/packages/testing/src/mocks/env-flags.mock.ts b/packages/testing/src/mocks/env-flags.mock.ts
index c0f3fb6ba51..cc94ae3941e 100644
--- a/packages/testing/src/mocks/env-flags.mock.ts
+++ b/packages/testing/src/mocks/env-flags.mock.ts
@@ -27,6 +27,7 @@ export interface EnvFlagsMockState {
isTriggerDevEnabled: boolean
isEnterpriseEnabled: boolean
isSsoEnabled: boolean
+ isUsageMonitoringEnabled: boolean
isAccessControlEnabled: boolean
isOrganizationsEnabled: boolean
isInboxEnabled: boolean
@@ -75,6 +76,7 @@ const defaultEnvFlagsState: EnvFlagsMockState = {
isTriggerDevEnabled: false,
isEnterpriseEnabled: false,
isSsoEnabled: false,
+ isUsageMonitoringEnabled: false,
isAccessControlEnabled: false,
isOrganizationsEnabled: false,
// True with billing off and no flags set — these carry a legacy default of
diff --git a/scripts/check-tool-registry-boundary.baseline.json b/scripts/check-tool-registry-boundary.baseline.json
index 8bf98124ed6..cd4e0371e60 100644
--- a/scripts/check-tool-registry-boundary.baseline.json
+++ b/scripts/check-tool-registry-boundary.baseline.json
@@ -382,16 +382,16 @@
"gateways": {}
},
"app/workspace/[workspaceId]/settings/[section]/page.tsx": {
- "modules": 2083,
+ "modules": 2130,
"gateways": {
"apps/sim/triggers/registry.ts": 472,
- "apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx": 451,
- "apps/sim/blocks/registry.ts": 339,
- "apps/sim/lib/auth/index.ts": 305,
+ "apps/sim/app/workspace/[workspaceId]/settings/[section]/settings.tsx": 470,
+ "apps/sim/blocks/registry.ts": 342,
+ "apps/sim/lib/auth/index.ts": 299,
"apps/sim/lib/webhooks/providers/index.ts": 109,
"apps/sim/lib/webhooks/providers/registry.ts": 107,
"apps/sim/hooks/selectors/registry.ts": 71,
- "apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx": 45
+ "apps/sim/app/workspace/[workspaceId]/settings/[section]/prefetch.ts": 51
}
},
"app/workspace/[workspaceId]/settings/billing/credit-usage/layout.tsx": {
@@ -457,6 +457,27 @@
"apps/sim/app/workspace/[workspaceId]/settings/secrets/[credentialId]/secret-detail.tsx": 77
}
},
+ "app/workspace/[workspaceId]/settings/usage/events/layout.tsx": {
+ "modules": 4,
+ "gateways": {}
+ },
+ "app/workspace/[workspaceId]/settings/usage/events/loading.tsx": {
+ "modules": 12,
+ "gateways": {}
+ },
+ "app/workspace/[workspaceId]/settings/usage/events/page.tsx": {
+ "modules": 1626,
+ "gateways": {
+ "apps/sim/lib/auth/index.ts": 1482,
+ "apps/sim/blocks/registry.ts": 535,
+ "apps/sim/blocks/registry-maps.ts": 532,
+ "apps/sim/triggers/index.ts": 474,
+ "apps/sim/triggers/registry.ts": 472,
+ "apps/sim/blocks/blocks/credential-group.ts": 187,
+ "apps/sim/stores/workflows/registry/store.ts": 169,
+ "apps/sim/hooks/queries/deployments.ts": 161
+ }
+ },
"app/workspace/[workspaceId]/skills/[skillId]/page.tsx": {
"modules": 1354,
"gateways": {