From 7fa116ccb45bb97ecaec1bf67ed0eba0cf58428d Mon Sep 17 00:00:00 2001 From: Linghan Hu Date: Tue, 7 Jul 2026 10:28:50 +0000 Subject: [PATCH 1/3] feat: align teams integration with slack parity --- .../src/signals/external/teams-bot-client.ts | 113 +++ .../internal-callbacks-teams.test.ts | 33 + .../routes/__tests__/zero-teams-bot.test.ts | 238 +++++- .../__tests__/zero-teams-connect.test.ts | 130 ++++ .../api/src/signals/routes/zero-teams-bot.ts | 84 ++- .../routes/zero-teams-browser-connect.ts | 9 + .../src/signals/routes/zero-teams-connect.ts | 5 + ...internal-teams-org-run-callback.service.ts | 50 ++ .../services/zero-teams-connect.service.ts | 710 ++++++++++++++---- .../services/zero-teams-dispatch.service.ts | 352 +++++++-- .../zero-page/teams-connect-signals.ts | 2 + .../zero-teams-connect-page.test.tsx | 2 + .../contracts/zero-teams-browser-connect.ts | 1 + .../src/contracts/zero-teams-connect.ts | 10 + 14 files changed, 1524 insertions(+), 215 deletions(-) diff --git a/turbo/apps/api/src/signals/external/teams-bot-client.ts b/turbo/apps/api/src/signals/external/teams-bot-client.ts index 8f7d7d98e65..e32e504e02c 100644 --- a/turbo/apps/api/src/signals/external/teams-bot-client.ts +++ b/turbo/apps/api/src/signals/external/teams-bot-client.ts @@ -21,6 +21,12 @@ const teamsActivityResponseSchema = z }) .passthrough(); +const teamsConversationResponseSchema = z + .object({ + id: z.string().min(1), + }) + .passthrough(); + const teamsGraphIdentitySchema = z .object({ id: z.string().nullable().optional(), @@ -67,6 +73,10 @@ type SendTeamsActivityResult = | { readonly kind: "ok"; readonly activityId: string | undefined } | TeamsApiErrorResult; +type CreateTeamsConversationResult = + | { readonly kind: "ok"; readonly conversationId: string } + | TeamsApiErrorResult; + type FetchTeamsGraphMessageResult = | { readonly kind: "ok"; readonly message: TeamsGraphMessage } | TeamsApiErrorResult; @@ -118,6 +128,20 @@ interface TeamsActivityBody { }; } +interface TeamsConversationIdentity { + readonly id: string; + readonly name?: string; +} + +interface TeamsCreateConversationBody { + readonly bot: TeamsConversationIdentity; + readonly members: readonly TeamsConversationIdentity[]; + readonly isGroup: false; + readonly channelData: { + readonly tenant: { readonly id: string }; + }; +} + function teamsBotCredentials(): TeamsBotCredentials | undefined { const appId = env("MICROSOFT_TEAMS_BOT_APP_ID"); const appPassword = env("MICROSOFT_TEAMS_BOT_APP_PASSWORD"); @@ -256,6 +280,18 @@ function teamsConversationActivityUrl(args: { : base; } +function teamsConversationsUrl(serviceUrl: string): string | undefined { + const parsed = safeUrlParse(serviceUrl); + if ( + !parsed || + (parsed.protocol !== "https:" && parsed.protocol !== "http:") + ) { + return undefined; + } + + return `${parsed.href.replace(/\/+$/u, "")}/v3/conversations`; +} + function teamsGraphChannelMessageUrl(args: { readonly teamId: string; readonly channelId: string; @@ -387,6 +423,83 @@ async function postTeamsActivity(args: { }; } +export async function createTeamsPersonalConversation(args: { + readonly serviceUrl: string; + readonly tenantId: string; + readonly botId: string; + readonly botName?: string | null; + readonly teamsUserId: string; + readonly teamsUserDisplayName?: string | null; + readonly signal: AbortSignal; +}): Promise { + const accessToken = await fetchTeamsBotAccessToken({ + tenantId: args.tenantId, + signal: args.signal, + }); + if (accessToken.kind === "teams-error") { + return accessToken; + } + + const url = teamsConversationsUrl(args.serviceUrl); + if (!url) { + return teamsApiError(400, "Invalid Microsoft Teams serviceUrl"); + } + + const body: TeamsCreateConversationBody = { + bot: { + id: args.botId, + ...(args.botName ? { name: args.botName } : {}), + }, + members: [ + { + id: args.teamsUserId, + ...(args.teamsUserDisplayName + ? { name: args.teamsUserDisplayName } + : {}), + }, + ], + isGroup: false, + channelData: { + tenant: { id: args.tenantId }, + }, + }; + + const responseResult = await settle( + fetch(url, { + method: "POST", + headers: { + authorization: `Bearer ${accessToken.accessToken}`, + "content-type": "application/json", + }, + body: JSON.stringify(body), + signal: args.signal, + }), + args.signal, + ); + if (!responseResult.ok) { + return teamsApiError(502, networkErrorMessage(responseResult.error)); + } + + const response = responseResult.value; + const responseText = await response.text(); + args.signal.throwIfAborted(); + if (!response.ok) { + return teamsApiError( + response.status, + responseText || `Microsoft Teams API returned HTTP ${response.status}`, + ); + } + + const parsed = teamsConversationResponseSchema.safeParse( + safeJsonParse(responseText), + ); + if (!parsed.success) { + return teamsApiError(502, "Invalid Microsoft Teams conversation response"); + } + + return { kind: "ok", conversationId: parsed.data.id }; +} + export function sendTeamsMessageReply(args: { readonly serviceUrl: string; readonly conversationId: string; diff --git a/turbo/apps/api/src/signals/routes/__tests__/internal-callbacks-teams.test.ts b/turbo/apps/api/src/signals/routes/__tests__/internal-callbacks-teams.test.ts index 2854b0aa68b..ccdcaf1e973 100644 --- a/turbo/apps/api/src/signals/routes/__tests__/internal-callbacks-teams.test.ts +++ b/turbo/apps/api/src/signals/routes/__tests__/internal-callbacks-teams.test.ts @@ -159,6 +159,22 @@ function teamsApiMocks(args: { return { tokenRequests, postedActivities }; } +function mockOpenRouterSummary(summary: string): unknown[] { + const requests: unknown[] = []; + server.use( + http.post( + "https://openrouter.ai/api/v1/chat/completions", + async ({ request }) => { + requests.push(await request.json()); + return HttpResponse.json({ + choices: [{ message: { content: summary } }], + }); + }, + ), + ); + return requests; +} + function dispatchRunId(body: unknown): string { const response = recordFromUnknown( body, @@ -350,6 +366,7 @@ async function claimFollowUpInThread(args: { beforeEach(() => { setupTeamsConnectTestEnv(APP_URL); mockEnv("MICROSOFT_TEAMS_BOT_APP_PASSWORD", BOT_APP_PASSWORD); + mockOptionalEnv("OPENROUTER_API_KEY", undefined); mockEnv("VM0_WEB_URL", "https://www.vm0.test"); mockEnv("VM0_API_URL", "https://api.vm0.test"); mockOptionalEnv("RUNNER_DEFAULT_GROUP", "vm0/test"); @@ -364,6 +381,8 @@ describe("Teams org internal callbacks", () => { it("posts completed run replies and persists Teams thread sessions", async () => { const teams = await setupConnectedTeamsActor({ zeroDebug: true }); const teamsApi = teamsApiMocks({ serviceUrl: teams.fixture.serviceUrl }); + mockOptionalEnv("OPENROUTER_API_KEY", "teams-summary-key"); + const summaryRequests = mockOpenRouterSummary("Teams completed summary"); const runId = await dispatchTeamsRun({ fixture: teams.fixture, activityId: "activity-completed-1", @@ -401,6 +420,20 @@ describe("Teams org internal callbacks", () => { expect(teamsApi.postedActivities[0]?.text).toContain( `[View run details](${APP_URL}/activities/${runId})`, ); + expect(teamsApi.postedActivities[0]?.text).toContain( + "Reply to Ada Lovelace", + ); + expect(summaryRequests).toHaveLength(1); + const summaryRequest = recordFromUnknown( + summaryRequests[0], + "Expected OpenRouter summary request", + ); + expect(JSON.stringify(summaryRequest.messages)).toContain( + "teams agent run", + ); + expect(JSON.stringify(summaryRequest.messages)).toContain( + "finish the task", + ); const followUpClaim = await claimFollowUpInThread({ fixture: teams.fixture, diff --git a/turbo/apps/api/src/signals/routes/__tests__/zero-teams-bot.test.ts b/turbo/apps/api/src/signals/routes/__tests__/zero-teams-bot.test.ts index 6ff7e5200e3..9c46b31af08 100644 --- a/turbo/apps/api/src/signals/routes/__tests__/zero-teams-bot.test.ts +++ b/turbo/apps/api/src/signals/routes/__tests__/zero-teams-bot.test.ts @@ -39,9 +39,10 @@ const TEAMS_APP_TENANT_ID = "11111111-1111-1111-1111-111111111111"; const SERVICE_URL = "https://smba.trafficmanager.net/amer/"; const APP_ORIGIN = "https://app.vm0.test"; const KEY_ID = "teams-test-key"; -const TEAMS_LOGIN_PROMPT_FALLBACK_TEXT = "Please connect your account first"; +const TEAMS_LOGIN_PROMPT_FALLBACK_TEXT = + "Please connect your account to use Zero in this Teams workspace."; const TEAMS_LOGIN_PROMPT_CARD_TEXT = - "To use Zero in Teams, please connect your account first."; + "Please connect your account to use Zero in this Teams workspace."; const BOT_FRAMEWORK_METADATA_URL = "https://login.botframework.com/v1/.well-known/openidconfiguration"; const BOT_FRAMEWORK_KEYS_URL = @@ -431,6 +432,72 @@ async function connectTeamsFixture( ); } +function teamsPersonalMessageActivity(args: { + readonly fixture: TeamsConnectFixture; + readonly id: string; + readonly text: string; +}): Record { + return teamsMessageActivity(args.fixture, { + id: args.id, + conversation: { + id: `a:personal-${args.fixture.teamsUserId}`, + conversationType: "personal", + }, + channelData: { + tenant: { + id: args.fixture.teamsTenantId, + name: args.fixture.teamsTenantName, + }, + teamsAppId: "teams-app-test", + }, + text: args.text, + entities: [], + replyToId: null, + }); +} + +async function setupConnectedTeamsBotActor(): Promise<{ + readonly fixture: TeamsConnectFixture; + readonly actor: ReturnType; + readonly runnerGroup: string; + readonly outboundRequests: TeamsOutboundRequest[]; +}> { + const fixture = await trackTeamsFixture( + Promise.resolve(teamsConnectFixture()), + ); + const actor = authOrgApi.user({ + userId: fixture.userId, + orgId: fixture.orgId, + orgRole: "org:admin", + }); + const runnerGroup = runsApi.configureRunnerGroup(); + context.mocks.ably.publish.mockResolvedValue(undefined); + authOrgApi.acceptAgentStorageWrites(); + runsApi.acceptStorageDownloads(); + runsApi.acceptTelemetryIngest(); + const agent = await authOrgApi.createAgent(actor, { + displayName: "Teams default agent", + visibility: "public", + }); + await authOrgApi.setDefaultAgent(actor, agent.agentId); + await runsApi.grantProEntitlement(actor); + await runsApi.ensureOrgModelProvider(actor); + botFrameworkHandlers(); + const outboundRequests = teamsOutboundHandlers( + fixture.serviceUrl, + fixture.teamsTenantId, + ); + + const installResponse = await postTeamsActivity({ + activity: teamsMessageActivity(fixture), + token: teamsToken(), + }); + expect(installResponse.status).toBe(200); + await connectTeamsFixture(fixture); + + return { fixture, actor, runnerGroup, outboundRequests }; +} + function dispatchRunId(dispatch: unknown): string { if (typeof dispatch !== "object" || dispatch === null) { throw new Error("Expected Teams dispatch object"); @@ -551,6 +618,7 @@ describe("POST /api/zero/teams/bot", () => { expect(connectUrl.searchParams.get("upn")).toBeNull(); expect(connectUrl.searchParams.get("teamId")).toBe("team-1"); expect(connectUrl.searchParams.get("teamName")).toBe("Team One"); + expect(connectUrl.searchParams.get("conversationType")).toBe("channel"); expect(body.dispatch).toMatchObject({ kind: "notice", connectUrl: expect.stringContaining(`${APP_ORIGIN}/settings/teams`), @@ -757,6 +825,172 @@ describe("POST /api/zero/teams/bot", () => { }); }); + it("handles connected Teams bot commands", async () => { + const { fixture, outboundRequests } = await setupConnectedTeamsBotActor(); + outboundRequests.splice(0, outboundRequests.length); + + const helpResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-command-help", + text: "help", + }), + token: teamsToken(), + }); + expect(helpResponse.status).toBe(200); + await expect(helpResponse.json()).resolves.toMatchObject({ + dispatch: { + kind: "notice", + replyText: expect.stringContaining("Zero Teams Bot Help"), + }, + }); + + const switchResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-command-switch", + text: "/zero switch", + }), + token: teamsToken(), + }); + expect(switchResponse.status).toBe(200); + await expect(switchResponse.json()).resolves.toMatchObject({ + dispatch: { + kind: "notice", + replyText: expect.stringContaining("/works"), + }, + }); + + const modelResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-command-model", + text: "zero model", + }), + token: teamsToken(), + }); + expect(modelResponse.status).toBe(200); + await expect(modelResponse.json()).resolves.toMatchObject({ + dispatch: { + kind: "notice", + replyText: expect.stringContaining("Choose the model"), + }, + }); + + const disconnectResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-command-disconnect", + text: "disconnect", + }), + token: teamsToken(), + }); + expect(disconnectResponse.status).toBe(200); + await expect(disconnectResponse.json()).resolves.toMatchObject({ + dispatch: { + kind: "notice", + replyText: expect.stringContaining("You have been disconnected"), + }, + }); + + expect(outboundRequests).toHaveLength(4); + expect( + outboundRequests.map((request) => { + return request.activityId; + }), + ).toStrictEqual([ + "activity-command-help", + "activity-command-switch", + "activity-command-model", + "activity-command-disconnect", + ]); + expect(outboundRequests[0]?.body).toMatchObject({ + type: "message", + text: expect.stringContaining("Zero Teams Bot Help"), + }); + expect(outboundRequests[3]?.body).toMatchObject({ + type: "message", + text: expect.stringContaining("agent access has been revoked"), + }); + }); + + it("replies when a connected Teams run is queued", async () => { + const { fixture, actor, outboundRequests } = + await setupConnectedTeamsBotActor(); + outboundRequests.splice(0, outboundRequests.length); + + const firstResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-queue-active-1", + text: "active run one", + }), + token: teamsToken(), + }); + expect(firstResponse.status).toBe(200); + const firstRunId = dispatchRunId((await firstResponse.json()).dispatch); + + const secondResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-queue-active-2", + text: "active run two", + }), + token: teamsToken(), + }); + expect(secondResponse.status).toBe(200); + const secondRunId = dispatchRunId((await secondResponse.json()).dispatch); + + const queuedResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-queue-third", + text: "queued run three", + }), + token: teamsToken(), + }); + expect(queuedResponse.status).toBe(200); + const queuedBody = await queuedResponse.json(); + expect(queuedBody.dispatch).toMatchObject({ + kind: "queued", + runId: expect.any(String), + }); + const queuedRunId = dispatchRunId(queuedBody.dispatch); + + expect(outboundRequests).toHaveLength(1); + expect(outboundRequests[0]).toMatchObject({ + activityId: "activity-queue-third", + body: { + type: "message", + summary: expect.stringContaining("Run queued"), + attachments: [ + { + contentType: "application/vnd.microsoft.card.adaptive", + content: { + type: "AdaptiveCard", + version: "1.4", + body: expect.arrayContaining([ + expect.objectContaining({ text: "Run queued" }), + ]), + actions: [ + { + type: "Action.OpenUrl", + title: "View queue", + url: `${APP_ORIGIN}/?queue=1`, + }, + ], + }, + }, + ], + }, + }); + expect(outboundRequests[0]?.body).not.toHaveProperty("text"); + + await runsApi.requestCancelRun(actor, queuedRunId, [200]); + await runsApi.requestCancelRun(actor, firstRunId, [200]); + await runsApi.requestCancelRun(actor, secondRunId, [200]); + }); + it("dispatches connected Teams messages to the org default agent", async () => { const fixture = await trackTeamsFixture( Promise.resolve(teamsConnectFixture()), diff --git a/turbo/apps/api/src/signals/routes/__tests__/zero-teams-connect.test.ts b/turbo/apps/api/src/signals/routes/__tests__/zero-teams-connect.test.ts index e826683938d..4a750ce0a52 100644 --- a/turbo/apps/api/src/signals/routes/__tests__/zero-teams-connect.test.ts +++ b/turbo/apps/api/src/signals/routes/__tests__/zero-teams-connect.test.ts @@ -1,6 +1,9 @@ import { describe, expect, it, beforeEach } from "vitest"; import { zeroTeamsConnectContract } from "@vm0/api-contracts/contracts/zero-teams-connect"; +import { HttpResponse, http } from "msw"; +import { mockEnv } from "../../../lib/env"; +import { server } from "../../../mocks/server"; import { accept, setupApp, testContext } from "../../../__tests__/test-helpers"; import { createFixtureTracker, @@ -17,6 +20,16 @@ import { const context = testContext(); const mocks = createZeroRouteMocks(context); const TEAMS_APP_TENANT_ID = "11111111-1111-1111-1111-111111111111"; +const BOT_APP_ID = "00000000-0000-0000-0000-000000000001"; +const BOT_APP_PASSWORD = "teams-test-password"; +const BOT_FRAMEWORK_SCOPE = "https://api.botframework.com/.default"; +const MICROSOFT_TOKEN_URL = + "https://login.microsoftonline.com/:tenantId/oauth2/v2.0/token"; + +interface TeamsWelcomeRequest { + readonly kind: "conversation" | "activity"; + readonly body: unknown; +} function teamsInstallUrl(tenantId?: string): string { const url = new URL( @@ -57,6 +70,50 @@ function connectBody( }; } +function teamsServiceBaseUrl(serviceUrl: string): string { + return serviceUrl.replace(/\/+$/u, ""); +} + +function teamsWelcomeHandlers( + fixture: TeamsConnectFixture, +): TeamsWelcomeRequest[] { + const requests: TeamsWelcomeRequest[] = []; + const serviceBaseUrl = teamsServiceBaseUrl(fixture.serviceUrl); + + server.use( + http.post(MICROSOFT_TOKEN_URL, async ({ request }) => { + const form = new URLSearchParams(await request.text()); + expect(form.get("client_id")).toBe(BOT_APP_ID); + expect(form.get("client_secret")).toBe(BOT_APP_PASSWORD); + expect(form.get("scope")).toBe(BOT_FRAMEWORK_SCOPE); + return HttpResponse.json({ + access_token: "teams-access-token", + token_type: "Bearer", + expires_in: 3600, + }); + }), + http.post(`${serviceBaseUrl}/v3/conversations`, async ({ request }) => { + requests.push({ + kind: "conversation", + body: await request.json(), + }); + return HttpResponse.json({ id: "a:teams-welcome-conversation" }); + }), + http.post( + `${serviceBaseUrl}/v3/conversations/:conversationId/activities`, + async ({ request }) => { + requests.push({ + kind: "activity", + body: await request.json(), + }); + return HttpResponse.json({ id: "teams-welcome-activity" }); + }, + ), + ); + + return requests; +} + async function seedTeamsInstallation( track: ( fixturePromise: Promise, @@ -187,6 +244,13 @@ describe("GET /api/zero/integrations/teams/connect", () => { teamId: fixture.teamsTeamId, teamName: fixture.teamsTeamName, defaultAgentName: null, + agentOrgSlug: null, + environment: { + requiredSecrets: [], + requiredVars: [], + missingSecrets: [], + missingVars: [], + }, }); }); }); @@ -328,6 +392,72 @@ describe("POST /api/zero/integrations/teams/connect", () => { expect(second.body.connectionId).toBe(first.body.connectionId); }); + + it("sends a one-time Teams welcome message after connect", async () => { + const fixture = await seedTeamsInstallation(track); + mocks.clerk.session(fixture.userId, fixture.orgId, "org:admin"); + mockEnv("MICROSOFT_TEAMS_BOT_APP_PASSWORD", BOT_APP_PASSWORD); + const welcomeRequests = teamsWelcomeHandlers(fixture); + + const client = setupApp({ context })(zeroTeamsConnectContract); + const body = { + ...connectBody(fixture), + conversationId: "a:personal-conversation", + conversationType: "personal", + activityId: "activity-connect", + }; + await accept( + client.connect({ + headers: { authorization: "Bearer clerk-session" }, + body, + }), + [200], + ); + await accept( + client.connect({ + headers: { authorization: "Bearer clerk-session" }, + body, + }), + [200], + ); + + expect(welcomeRequests).toHaveLength(2); + expect(welcomeRequests[0]).toMatchObject({ + kind: "conversation", + body: { + bot: { id: "28:bot-1", name: "Zero" }, + members: [{ id: fixture.teamsUserId, name: "Ada Lovelace" }], + isGroup: false, + channelData: { + tenant: { id: fixture.teamsTenantId }, + }, + }, + }); + expect(welcomeRequests[1]).toMatchObject({ + kind: "activity", + body: { + type: "message", + summary: "You're connected!", + attachments: [ + { + contentType: "application/vnd.microsoft.card.adaptive", + content: { + type: "AdaptiveCard", + version: "1.4", + body: expect.arrayContaining([ + expect.objectContaining({ + text: expect.stringContaining("You're connected"), + }), + expect.objectContaining({ + text: expect.stringContaining("Hi! I'm Zero"), + }), + ]), + }, + }, + ], + }, + }); + }); }); describe("DELETE /api/zero/integrations/teams/connect", () => { diff --git a/turbo/apps/api/src/signals/routes/zero-teams-bot.ts b/turbo/apps/api/src/signals/routes/zero-teams-bot.ts index 4c907958133..d764b0ade95 100644 --- a/turbo/apps/api/src/signals/routes/zero-teams-bot.ts +++ b/turbo/apps/api/src/signals/routes/zero-teams-bot.ts @@ -6,6 +6,7 @@ import { readTeamsActivityChannelId, readTeamsActivityServiceUrl, } from "../../lib/teams-bot-activity"; +import { env } from "../../lib/env"; import { verifyTeamsBotAuthorization } from "../../lib/teams-bot-auth"; import { logger } from "../../lib/log"; import { authorization$, request$ } from "../context/hono"; @@ -25,7 +26,7 @@ import { safeJsonParse } from "../utils"; const L = logger("TeamsBot"); const TEAMS_LOGIN_PROMPT_CARD_TEXT = - "To use Zero in Teams, please connect your account first."; + "Please connect your account to use Zero in this Teams workspace."; function errorResponse( status: 400 | 401 | 403 | 503, @@ -73,6 +74,42 @@ function buildTeamsLoginPromptCard(args: { }; } +function queueUrl(): string { + return `${env("APP_URL")}/?queue=1`; +} + +function buildTeamsQueueText(url: string): string { + return `\u26a0 Run queued -- concurrency limit reached. Will start automatically when a slot is available. [View queue](${url})`; +} + +function buildTeamsQueueCard(args: { + readonly url: string; +}): TeamsAdaptiveCard { + return { + type: "AdaptiveCard", + version: "1.4", + body: [ + { + type: "TextBlock", + text: "Run queued", + wrap: true, + }, + { + type: "TextBlock", + text: "Concurrency limit reached. Will start automatically when a slot is available.", + wrap: true, + }, + ], + actions: [ + { + type: "Action.OpenUrl", + title: "View queue", + url: args.url, + }, + ], + }; +} + const handleZeroTeamsBot$ = command( async ({ get, set }, signal: AbortSignal) => { const request = get(request$); @@ -141,23 +178,44 @@ const handleZeroTeamsBot$ = command( ); signal.throwIfAborted(); - if ( - normalized.activity.kind === "message" && - (dispatch.kind === "notice" || dispatch.kind === "failed") - ) { + if (normalized.activity.kind === "message") { + const queueNoticeUrl = dispatch.kind === "queued" ? queueUrl() : null; + const replyText = + dispatch.kind === "notice" || dispatch.kind === "failed" + ? dispatch.replyText + : queueNoticeUrl + ? buildTeamsQueueText(queueNoticeUrl) + : null; + const card = + dispatch.kind === "notice" && dispatch.connectUrl + ? buildTeamsLoginPromptCard({ + connectUrl: dispatch.connectUrl, + }) + : queueNoticeUrl + ? buildTeamsQueueCard({ url: queueNoticeUrl }) + : undefined; + if (!replyText) { + return { + status: 200 as const, + body: { + ok: true as const, + activity: normalized.activity, + connectUrl: buildTeamsConnectUrlForActivity({ + activity: normalized.activity, + installation, + }), + dispatch, + }, + }; + } + const reply = await sendTeamsMessageReply({ serviceUrl: normalized.activity.serviceUrl, conversationId: normalized.activity.conversationId, activityId: normalized.activity.activityId ?? undefined, tenantId: normalized.activity.tenantId, - text: dispatch.replyText, - ...(dispatch.kind === "notice" && dispatch.connectUrl - ? { - card: buildTeamsLoginPromptCard({ - connectUrl: dispatch.connectUrl, - }), - } - : {}), + text: replyText, + ...(card ? { card } : {}), signal, }); signal.throwIfAborted(); diff --git a/turbo/apps/api/src/signals/routes/zero-teams-browser-connect.ts b/turbo/apps/api/src/signals/routes/zero-teams-browser-connect.ts index 2f6e2aa87c9..06e3bc898bb 100644 --- a/turbo/apps/api/src/signals/routes/zero-teams-browser-connect.ts +++ b/turbo/apps/api/src/signals/routes/zero-teams-browser-connect.ts @@ -43,6 +43,7 @@ function teamsSettingsParams( readonly teamName?: string; readonly serviceUrl?: string; readonly conversationId?: string; + readonly conversationType?: string; readonly activityId?: string; readonly channelId?: string; readonly threadId?: string; @@ -92,6 +93,9 @@ function teamsSettingsParams( if (query.conversationId) { params.set("conversationId", query.conversationId); } + if (query.conversationType) { + params.set("conversationType", query.conversationType); + } if (query.activityId) { params.set("activityId", query.activityId); } @@ -236,6 +240,11 @@ const browserConnect$ = command(async ({ get, set }, signal: AbortSignal) => { teamId: query.teamId ?? installation.teamsTeamId ?? undefined, teamName: query.teamName ?? installation.teamsTeamName ?? undefined, serviceUrl: query.serviceUrl ?? installation.serviceUrl ?? undefined, + conversationId: query.conversationId, + conversationType: query.conversationType, + activityId: query.activityId, + channelId: query.channelId, + threadId: query.threadId, }, signal, ); diff --git a/turbo/apps/api/src/signals/routes/zero-teams-connect.ts b/turbo/apps/api/src/signals/routes/zero-teams-connect.ts index 0c079f31f18..951645e1210 100644 --- a/turbo/apps/api/src/signals/routes/zero-teams-connect.ts +++ b/turbo/apps/api/src/signals/routes/zero-teams-connect.ts @@ -64,6 +64,11 @@ const connectInner$ = command(async ({ get, set }, signal: AbortSignal) => { teamId: body.teamId, teamName: body.teamName, serviceUrl: body.serviceUrl, + conversationId: body.conversationId, + conversationType: body.conversationType, + activityId: body.activityId, + channelId: body.channelId, + threadId: body.threadId, }, signal, ); diff --git a/turbo/apps/api/src/signals/services/internal-teams-org-run-callback.service.ts b/turbo/apps/api/src/signals/services/internal-teams-org-run-callback.service.ts index c3df2cd754d..3b4f6c8fcee 100644 --- a/turbo/apps/api/src/signals/services/internal-teams-org-run-callback.service.ts +++ b/turbo/apps/api/src/signals/services/internal-teams-org-run-callback.service.ts @@ -36,6 +36,7 @@ import type { } from "./internal-run-callback"; import { formatRunErrorForRunOwner$ } from "./run-error-format.service"; import { getRunOutputText } from "./run-output.service"; +import { saveRunSummary, saveRunSummary$ } from "./run-summary.service"; import { teamsOrgCallbackPayloadSchema, type TeamsOrgCallbackPayload, @@ -248,6 +249,15 @@ async function resolveFooterText(args: { if (respondedBy) { parts.push(respondedBy); } + if (args.payload.conversationType !== "personal") { + const replyTo = + args.payload.teamsUserDisplayName ?? + args.payload.teamsUserPrincipalName ?? + args.payload.teamsUserId; + if (replyTo) { + parts.push(`Reply to ${replyTo}`); + } + } if (modelLabel) { parts.push(modelLabel); } @@ -454,6 +464,11 @@ async function handleCompletion(args: { readonly chatThreadId: string | null | undefined; readonly errorMessage: string; }) => Promise; + readonly saveRunSummary: ( + runId: string, + prompt: string, + resultText: string, + ) => Promise; readonly signal: AbortSignal; }): Promise { const installation = await loadInstallation({ @@ -552,6 +567,11 @@ async function handleCompletion(args: { signal: args.signal, }); + if (run?.prompt) { + await args.saveRunSummary(args.runId, run.prompt, output ?? ""); + args.signal.throwIfAborted(); + } + L.debug("Teams org callback processed successfully", { runId: args.runId }); return successResponse(); } @@ -568,6 +588,11 @@ interface HandleTeamsOrgInternalCallbackInput { readonly chatThreadId: string | null | undefined; readonly errorMessage: string; }) => Promise; + readonly saveRunSummary: ( + runId: string, + prompt: string, + resultText: string, + ) => Promise; readonly signal?: AbortSignal; } @@ -602,6 +627,7 @@ async function handleTeamsOrgInternalCallback( payload, getFeatureOverrides: input.getFeatureOverrides, formatRunError: input.formatRunError, + saveRunSummary: input.saveRunSummary, signal, }); } @@ -621,6 +647,18 @@ export const handleTeamsOrgInternalCallback$ = command( formatRunError: (params) => { return set(formatRunErrorForRunOwner$, params, signal); }, + saveRunSummary: (runId, prompt, resultText) => { + return set( + saveRunSummary$, + { + runId, + triggerSource: "teams", + prompt, + resultText, + }, + signal, + ); + }, signal, }); return dispatchResultFromResponse(result); @@ -648,6 +686,18 @@ export async function handleTeamsOrgInternalCallbackWithoutCcstate( }), ); }, + saveRunSummary: async (runId, prompt, resultText) => { + await saveRunSummary( + db, + { + runId, + triggerSource: "teams", + prompt, + resultText, + }, + signal, + ); + }, signal, }); return dispatchResultFromResponse(result); diff --git a/turbo/apps/api/src/signals/services/zero-teams-connect.service.ts b/turbo/apps/api/src/signals/services/zero-teams-connect.service.ts index eaac8f92a62..62d40937d60 100644 --- a/turbo/apps/api/src/signals/services/zero-teams-connect.service.ts +++ b/turbo/apps/api/src/signals/services/zero-teams-connect.service.ts @@ -1,5 +1,11 @@ import { command, computed, type Computed } from "ccstate"; -import { agentComposes } from "@vm0/db/schema/agent-compose"; +import { guaranteedConnectorProvidedBindingNames } from "@vm0/api-contracts/contracts/connector-schemas"; +import { extractAndGroupVariables } from "@vm0/core/variable-expander"; +import { + agentComposes, + agentComposeVersions, +} from "@vm0/db/schema/agent-compose"; +import { orgCache } from "@vm0/db/schema/org-cache"; import { orgMembersCache } from "@vm0/db/schema/org-members-cache"; import { orgMetadata } from "@vm0/db/schema/org-metadata"; import { teamsOrgConnections } from "@vm0/db/schema/teams-org-connection"; @@ -10,14 +16,24 @@ import type { TeamsInboundActivity } from "@vm0/api-contracts/contracts/zero-tea import { and, eq, isNull, sql } from "drizzle-orm"; import { env } from "../../lib/env"; +import { logger } from "../../lib/log"; import { db$, writeDb$, type Db, type ReadonlyDb } from "../external/db"; import { publishUserSignal } from "../external/realtime"; +import { + createTeamsPersonalConversation, + sendTeamsMessageReply, + type TeamsAdaptiveCard, +} from "../external/teams-bot-client"; import { nowDate } from "../external/time"; import { settle } from "../utils"; import { ensureUserArtifactStorage } from "./agent-run-storage.service"; +import { zeroConnectorList } from "./zero-connector-data.service"; +import { userSecrets, userVariables } from "./zero-user-data.service"; type TeamsInstallation = typeof teamsOrgInstallations.$inferSelect; +const L = logger("TeamsConnect"); + type TeamsConnectResult = | { readonly kind: "not_found"; readonly message: string } | { readonly kind: "forbidden"; readonly message: string } @@ -95,6 +111,7 @@ function buildTeamsBrowserConnectUrl(args: { readonly teamName?: string | null; readonly serviceUrl?: string | null; readonly conversationId?: string | null; + readonly conversationType?: string | null; readonly activityId?: string | null; readonly channelId?: string | null; readonly threadId?: string | null; @@ -116,6 +133,7 @@ function buildTeamsBrowserConnectUrl(args: { setOptionalParam(params, "teamName", args.teamName); setOptionalParam(params, "serviceUrl", args.serviceUrl); setOptionalParam(params, "conversationId", args.conversationId); + setOptionalParam(params, "conversationType", args.conversationType); setOptionalParam(params, "activityId", args.activityId); setOptionalParam(params, "channelId", args.channelId); setOptionalParam(params, "threadId", args.threadId); @@ -179,6 +197,7 @@ export function buildTeamsConnectUrlForActivity(args: { teamName: args.activity.teamName, serviceUrl: args.activity.serviceUrl, conversationId: args.activity.conversationId, + conversationType: args.activity.conversationType, activityId: args.activity.activityId, channelId: args.activity.channelId, threadId: args.activity.threadId, @@ -369,24 +388,239 @@ async function getTeamsAgentName( return agent?.displayName ?? agent?.name; } +interface TeamsEnvironment { + readonly requiredSecrets: readonly string[]; + readonly requiredVars: readonly string[]; + readonly missingSecrets: readonly string[]; + readonly missingVars: readonly string[]; +} + +type ConnectorProvidedBindings = Parameters< + typeof guaranteedConnectorProvidedBindingNames +>[0]["bindings"]; + +interface ConnectedTeamsStatusFields { + readonly defaultAgentName: string | null; + readonly agentOrgSlug: string | null; + readonly environment: TeamsEnvironment; +} + +interface TeamsConnectStatus { + readonly isInstalled: boolean; + readonly isConnected: boolean; + readonly isAdmin: boolean; + readonly installUrl?: string | null; + readonly connectUrl?: string | null; + readonly tenantId?: string | null; + readonly tenantName?: string | null; + readonly teamId?: string | null; + readonly teamName?: string | null; + readonly defaultAgentName?: string | null; + readonly agentOrgSlug?: string | null; + readonly environment?: TeamsEnvironment; +} + +function emptyTeamsEnvironment(): TeamsEnvironment { + return { + requiredSecrets: [], + requiredVars: [], + missingSecrets: [], + missingVars: [], + }; +} + +async function getTeamsAgentOrgSlug( + db: ReadonlyDb, + orgId: string, +): Promise { + const [orgCacheRow] = await db + .select({ slug: orgCache.slug }) + .from(orgCache) + .where(eq(orgCache.orgId, orgId)) + .limit(1); + return orgCacheRow?.slug ?? null; +} + +async function resolveTeamsEnvironment(args: { + readonly db: ReadonlyDb; + readonly orgId: string; + readonly loadUserSecretNames: () => Promise; + readonly loadUserVarNames: () => Promise; + readonly loadConnectorBindings: () => Promise; +}): Promise { + const [meta] = await args.db + .select({ defaultAgentId: orgMetadata.defaultAgentId }) + .from(orgMetadata) + .where(eq(orgMetadata.orgId, args.orgId)) + .limit(1); + + if (!meta?.defaultAgentId) { + return emptyTeamsEnvironment(); + } + + const [compose] = await args.db + .select({ headVersionId: agentComposes.headVersionId }) + .from(agentComposes) + .where(eq(agentComposes.id, meta.defaultAgentId)) + .limit(1); + + if (!compose?.headVersionId) { + return emptyTeamsEnvironment(); + } + + const [version] = await args.db + .select({ content: agentComposeVersions.content }) + .from(agentComposeVersions) + .where(eq(agentComposeVersions.id, compose.headVersionId)) + .limit(1); + + if (!version) { + return emptyTeamsEnvironment(); + } + + const grouped = extractAndGroupVariables(version.content); + const requiredSecrets = grouped.secrets.map((secret) => { + return secret.name; + }); + const requiredVars = grouped.vars.map((variable) => { + return variable.name; + }); + const [userSecretNames, userVarNames, connectorBindings] = await Promise.all([ + args.loadUserSecretNames(), + args.loadUserVarNames(), + args.loadConnectorBindings(), + ]); + const existingSecretNames = new Set([ + ...userSecretNames, + ...guaranteedConnectorProvidedBindingNames({ + bindings: connectorBindings, + namespace: "secrets", + }), + ]); + const existingVarNames = new Set([ + ...userVarNames, + ...guaranteedConnectorProvidedBindingNames({ + bindings: connectorBindings, + namespace: "vars", + }), + ]); + + return { + requiredSecrets, + requiredVars, + missingSecrets: requiredSecrets.filter((name) => { + return !existingSecretNames.has(name); + }), + missingVars: requiredVars.filter((name) => { + return !existingVarNames.has(name); + }), + }; +} + +async function resolveConnectedStatusFields(args: { + readonly db: ReadonlyDb; + readonly orgId: string; + readonly userId: string; + readonly loadUserSecretNames: () => Promise; + readonly loadUserVarNames: () => Promise; + readonly loadConnectorBindings: () => Promise; +}): Promise { + const composeId = await resolveEffectiveComposeId( + args.db, + args.userId, + args.orgId, + ); + const [agentOrgSlug, environment] = await Promise.all([ + getTeamsAgentOrgSlug(args.db, args.orgId), + resolveTeamsEnvironment(args), + ]); + return { + defaultAgentName: composeId + ? ((await getTeamsAgentName(args.db, composeId)) ?? null) + : null, + agentOrgSlug, + environment, + }; +} + +async function teamsConnectionForStatus(args: { + readonly db: ReadonlyDb; + readonly userId: string; + readonly tenantId: string; +}): Promise { + const [connection] = await args.db + .select() + .from(teamsOrgConnections) + .where( + and( + eq(teamsOrgConnections.vm0UserId, args.userId), + eq(teamsOrgConnections.teamsTenantId, args.tenantId), + ), + ) + .limit(1); + return connection; +} + +function inactiveTeamsStatus(args: { + readonly installation: TeamsInstallation | undefined; + readonly orgId: string; + readonly userId: string; + readonly isAdmin: boolean; +}): TeamsConnectStatus { + return { + isInstalled: false, + isConnected: false, + isAdmin: args.isAdmin, + installUrl: buildTeamsInstallUrl(args.installation?.teamsTenantId), + connectUrl: args.isAdmin + ? buildTeamsOauthConnectUrl({ + orgId: args.orgId, + userId: args.userId, + }) + : null, + }; +} + +function activeTeamsStatus(args: { + readonly installation: TeamsInstallation; + readonly connection: typeof teamsOrgConnections.$inferSelect | undefined; + readonly orgId: string; + readonly userId: string; + readonly isAdmin: boolean; + readonly connectedFields: ConnectedTeamsStatusFields | null; +}): TeamsConnectStatus { + const status: TeamsConnectStatus = { + isInstalled: true, + isConnected: Boolean(args.connection), + isAdmin: args.isAdmin, + installUrl: null, + connectUrl: args.connection + ? null + : buildTeamsOauthConnectUrl({ + orgId: args.orgId, + userId: args.userId, + }), + tenantId: args.installation.teamsTenantId, + tenantName: args.installation.teamsTenantName, + teamId: args.installation.teamsTeamId, + teamName: args.installation.teamsTeamName, + defaultAgentName: args.connectedFields?.defaultAgentName ?? null, + }; + if (!args.connectedFields) { + return status; + } + return { + ...status, + agentOrgSlug: args.connectedFields.agentOrgSlug, + environment: args.connectedFields.environment, + }; +} + export function zeroTeamsConnectStatus(args: { readonly orgId: string; readonly userId: string; readonly isAdmin: boolean; -}): Computed< - Promise<{ - readonly isInstalled: boolean; - readonly isConnected: boolean; - readonly isAdmin: boolean; - readonly installUrl?: string | null; - readonly connectUrl?: string | null; - readonly tenantId?: string | null; - readonly tenantName?: string | null; - readonly teamId?: string | null; - readonly teamName?: string | null; - readonly defaultAgentName?: string | null; - }> -> { +}): Computed> { return computed(async (get) => { const db = get(db$); const [installation] = await db @@ -396,60 +630,50 @@ export function zeroTeamsConnectStatus(args: { .limit(1); if (!installation || !isTeamsInstallationActive(installation)) { - return { - isInstalled: false, - isConnected: false, - isAdmin: args.isAdmin, - installUrl: buildTeamsInstallUrl(installation?.teamsTenantId), - connectUrl: args.isAdmin - ? buildTeamsOauthConnectUrl({ - orgId: args.orgId, - userId: args.userId, - }) - : null, - }; + return inactiveTeamsStatus({ ...args, installation }); } - const [connection] = await db - .select() - .from(teamsOrgConnections) - .where( - and( - eq(teamsOrgConnections.vm0UserId, args.userId), - eq(teamsOrgConnections.teamsTenantId, installation.teamsTenantId), - ), - ) - .limit(1); - - let defaultAgentName: string | null = null; - if (connection) { - const composeId = await resolveEffectiveComposeId( - db, - args.userId, - args.orgId, - ); - defaultAgentName = composeId - ? ((await getTeamsAgentName(db, composeId)) ?? null) - : null; - } - - return { - isInstalled: true, - isConnected: Boolean(connection), - isAdmin: args.isAdmin, - installUrl: null, - connectUrl: connection - ? null - : buildTeamsOauthConnectUrl({ - orgId: args.orgId, - userId: args.userId, - }), + const connection = await teamsConnectionForStatus({ + db, + userId: args.userId, tenantId: installation.teamsTenantId, - tenantName: installation.teamsTenantName, - teamId: installation.teamsTeamId, - teamName: installation.teamsTeamName, - defaultAgentName, - }; + }); + const connectedFields = connection + ? await resolveConnectedStatusFields({ + db, + orgId: args.orgId, + userId: args.userId, + loadUserSecretNames: async () => { + const list = await get( + userSecrets({ orgId: args.orgId, userId: args.userId }), + ); + return list.secrets.map((secret) => { + return secret.name; + }); + }, + loadUserVarNames: async () => { + const list = await get( + userVariables({ orgId: args.orgId, userId: args.userId }), + ); + return list.variables.map((variable) => { + return variable.name; + }); + }, + loadConnectorBindings: async () => { + const connectors = await get( + zeroConnectorList({ orgId: args.orgId, userId: args.userId }), + ); + return connectors.connectorProvidedBindings; + }, + }) + : null; + + return activeTeamsStatus({ + ...args, + installation, + connection, + connectedFields, + }); }); } @@ -499,8 +723,242 @@ type ConnectTeamsInstallationArgs = { readonly teamId?: string; readonly teamName?: string; readonly serviceUrl?: string; + readonly conversationId?: string; + readonly conversationType?: string; + readonly activityId?: string; + readonly channelId?: string; + readonly threadId?: string; }; +type BindTeamsInstallationResult = + | { readonly kind: "bound"; readonly installation: TeamsInstallation } + | { readonly kind: "not_found"; readonly message: string } + | { readonly kind: "forbidden"; readonly message: string }; + +function buildTeamsWelcomeCard(args: { + readonly agentName: string | undefined; +}): TeamsAdaptiveCard { + return { + type: "AdaptiveCard", + version: "1.4", + body: [ + { + type: "TextBlock", + text: "You're connected! Mention @Zero in a channel or send a DM to start chatting with your agent.", + wrap: true, + }, + { + type: "TextBlock", + text: "Hi! I'm Zero. I can connect you to AI agents to help with your tasks.", + wrap: true, + }, + { + type: "TextBlock", + text: args.agentName + ? `Workspace Agent\n- ${args.agentName}\n\nHow to Use\n- Just describe what you need help with` + : "No workspace agent configured yet.", + wrap: true, + }, + ], + }; +} + +async function resolveTeamsWelcomeConversationId(args: { + readonly tenantId: string; + readonly serviceUrl: string; + readonly teamsUserId: string | undefined; + readonly teamsUserDisplayName: string | undefined; + readonly botId: string | null; + readonly botName: string | null; + readonly conversationId: string | undefined; + readonly conversationType: string | undefined; + readonly signal: AbortSignal; +}): Promise { + if (args.teamsUserId && args.botId) { + const conversation = await createTeamsPersonalConversation({ + serviceUrl: args.serviceUrl, + tenantId: args.tenantId, + botId: args.botId, + botName: args.botName, + teamsUserId: args.teamsUserId, + teamsUserDisplayName: args.teamsUserDisplayName, + signal: args.signal, + }); + args.signal.throwIfAborted(); + if (conversation.kind === "ok") { + return conversation.conversationId; + } + L.warn("Failed to create Teams personal welcome conversation", { + tenantId: args.tenantId, + status: conversation.status, + error: conversation.error, + }); + } + + return args.conversationType === "personal" ? args.conversationId : undefined; +} + +async function notifyTeamsConnect(args: { + readonly db: Db; + readonly connectionId: string; + readonly installation: TeamsInstallation; + readonly orgId: string; + readonly tenantId: string; + readonly serviceUrl: string | undefined; + readonly conversationId: string | undefined; + readonly conversationType: string | undefined; + readonly teamsUserId: string | undefined; + readonly teamsUserDisplayName: string | undefined; + readonly signal: AbortSignal; +}): Promise { + const [connection] = await args.db + .select({ dmWelcomeSent: teamsOrgConnections.dmWelcomeSent }) + .from(teamsOrgConnections) + .where(eq(teamsOrgConnections.id, args.connectionId)) + .limit(1); + args.signal.throwIfAborted(); + + if (!connection || connection.dmWelcomeSent || !args.serviceUrl) { + return; + } + + const conversationId = await resolveTeamsWelcomeConversationId({ + tenantId: args.tenantId, + serviceUrl: args.serviceUrl, + teamsUserId: args.teamsUserId, + teamsUserDisplayName: args.teamsUserDisplayName, + botId: args.installation.botId, + botName: args.installation.botName, + conversationId: args.conversationId, + conversationType: args.conversationType, + signal: args.signal, + }); + args.signal.throwIfAborted(); + + if (!conversationId) { + return; + } + + const defaultComposeId = await resolveDefaultComposeId(args.db, args.orgId); + args.signal.throwIfAborted(); + const agentName = defaultComposeId + ? await getTeamsAgentName(args.db, defaultComposeId) + : undefined; + args.signal.throwIfAborted(); + + const sendResult = await sendTeamsMessageReply({ + serviceUrl: args.serviceUrl, + conversationId, + tenantId: args.tenantId, + text: "You're connected!", + card: buildTeamsWelcomeCard({ agentName }), + signal: args.signal, + }); + args.signal.throwIfAborted(); + + if (sendResult.kind === "teams-error") { + L.warn("Failed to send Teams connect welcome", { + tenantId: args.tenantId, + conversationId, + status: sendResult.status, + error: sendResult.error, + }); + return; + } + + await args.db + .update(teamsOrgConnections) + .set({ dmWelcomeSent: true, updatedAt: nowDate() }) + .where(eq(teamsOrgConnections.id, args.connectionId)); + args.signal.throwIfAborted(); +} + +async function bindUnclaimedTeamsInstallation(args: { + readonly db: Db; + readonly connectArgs: ConnectTeamsInstallationArgs; + readonly signal: AbortSignal; +}): Promise { + const [updated] = await args.db + .update(teamsOrgInstallations) + .set({ + orgId: args.connectArgs.orgId, + installedByUserId: args.connectArgs.userId, + updatedAt: nowDate(), + }) + .where( + and( + eq(teamsOrgInstallations.teamsTenantId, args.connectArgs.tenantId), + isNull(teamsOrgInstallations.orgId), + ), + ) + .returning(); + args.signal.throwIfAborted(); + + if (updated) { + return { kind: "bound", installation: updated }; + } + + const [existing] = await args.db + .select() + .from(teamsOrgInstallations) + .where(eq(teamsOrgInstallations.teamsTenantId, args.connectArgs.tenantId)) + .limit(1); + args.signal.throwIfAborted(); + if (!existing) { + return { kind: "not_found", message: installationNotFoundMessage }; + } + if (existing.orgId !== args.connectArgs.orgId) { + return { kind: "forbidden", message: orgMismatchMessage }; + } + return { kind: "bound", installation: existing }; +} + +async function finalizeTeamsConnection(args: { + readonly db: Db; + readonly connectArgs: ConnectTeamsInstallationArgs; + readonly installation: TeamsInstallation; + readonly role: "admin" | "member"; + readonly ensureArtifactStorage: () => Promise; + readonly signal: AbortSignal; +}): Promise> { + const { connectArgs } = args; + const connectionId = await upsertTeamsConnection(args.db, { + teamsUserId: connectArgs.teamsUserId, + teamsAadObjectId: connectArgs.teamsAadObjectId, + teamsTenantId: connectArgs.tenantId, + vm0UserId: connectArgs.userId, + teamsUserDisplayName: connectArgs.teamsUserDisplayName, + teamsUserPrincipalName: connectArgs.teamsUserPrincipalName, + }); + args.signal.throwIfAborted(); + + await args.ensureArtifactStorage(); + args.signal.throwIfAborted(); + + await notifyTeamsConnect({ + db: args.db, + connectionId, + installation: args.installation, + orgId: connectArgs.orgId, + tenantId: connectArgs.tenantId, + serviceUrl: + connectArgs.serviceUrl ?? args.installation.serviceUrl ?? undefined, + conversationId: connectArgs.conversationId, + conversationType: connectArgs.conversationType, + teamsUserId: connectArgs.teamsUserId, + teamsUserDisplayName: connectArgs.teamsUserDisplayName, + signal: args.signal, + }); + args.signal.throwIfAborted(); + + return { + kind: "ok", + connectionId, + role: args.role, + installation: args.installation, + }; +} + export const prepareTeamsInstallation$ = command( async ( { get, set }, @@ -613,65 +1071,32 @@ export const connectTeamsInstallation$ = command( }); signal.throwIfAborted(); - const [updated] = await writeDb - .update(teamsOrgInstallations) - .set({ - orgId: args.orgId, - installedByUserId: args.userId, - updatedAt: nowDate(), - }) - .where( - and( - eq(teamsOrgInstallations.teamsTenantId, args.tenantId), - isNull(teamsOrgInstallations.orgId), - ), - ) - .returning(); - signal.throwIfAborted(); - - let boundInstallation = updated; - if (!boundInstallation) { - const [existing] = await writeDb - .select() - .from(teamsOrgInstallations) - .where(eq(teamsOrgInstallations.teamsTenantId, args.tenantId)) - .limit(1); - signal.throwIfAborted(); - if (!existing) { - return { kind: "not_found", message: installationNotFoundMessage }; - } - if (existing.orgId !== args.orgId) { - return { kind: "forbidden", message: orgMismatchMessage }; - } - boundInstallation = existing; - } - - const connectionId = await upsertTeamsConnection(writeDb, { - teamsUserId: args.teamsUserId, - teamsAadObjectId: args.teamsAadObjectId, - teamsTenantId: args.tenantId, - vm0UserId: args.userId, - teamsUserDisplayName: args.teamsUserDisplayName, - teamsUserPrincipalName: args.teamsUserPrincipalName, + const bindResult = await bindUnclaimedTeamsInstallation({ + db: writeDb, + connectArgs: args, + signal, }); - signal.throwIfAborted(); - - await get( - ensureUserArtifactStorage({ - db: writeDb, - orgId: args.orgId, - userId: args.userId, - name: "artifact", - }), - ); - signal.throwIfAborted(); + if (bindResult.kind !== "bound") { + return bindResult; + } - return { - kind: "ok", - connectionId, + return finalizeTeamsConnection({ + db: writeDb, + connectArgs: args, + installation: bindResult.installation, role: "admin", - installation: boundInstallation, - }; + ensureArtifactStorage: () => { + return get( + ensureUserArtifactStorage({ + db: writeDb, + orgId: args.orgId, + userId: args.userId, + name: "artifact", + }), + ); + }, + signal, + }); } if (installation.orgId !== args.orgId) { @@ -686,32 +1111,23 @@ export const connectTeamsInstallation$ = command( }); signal.throwIfAborted(); - const connectionId = await upsertTeamsConnection(writeDb, { - teamsUserId: args.teamsUserId, - teamsAadObjectId: args.teamsAadObjectId, - teamsTenantId: args.tenantId, - vm0UserId: args.userId, - teamsUserDisplayName: args.teamsUserDisplayName, - teamsUserPrincipalName: args.teamsUserPrincipalName, - }); - signal.throwIfAborted(); - - await get( - ensureUserArtifactStorage({ - db: writeDb, - orgId: args.orgId, - userId: args.userId, - name: "artifact", - }), - ); - signal.throwIfAborted(); - - return { - kind: "ok", - connectionId, - role: args.orgRole, + return finalizeTeamsConnection({ + db: writeDb, + connectArgs: args, installation, - }; + role: args.orgRole, + ensureArtifactStorage: () => { + return get( + ensureUserArtifactStorage({ + db: writeDb, + orgId: args.orgId, + userId: args.userId, + name: "artifact", + }), + ); + }, + signal, + }); }, ); diff --git a/turbo/apps/api/src/signals/services/zero-teams-dispatch.service.ts b/turbo/apps/api/src/signals/services/zero-teams-dispatch.service.ts index accbe4ea06e..1b3b2a49df1 100644 --- a/turbo/apps/api/src/signals/services/zero-teams-dispatch.service.ts +++ b/turbo/apps/api/src/signals/services/zero-teams-dispatch.service.ts @@ -12,6 +12,7 @@ import type { TeamsInboundActivity } from "@vm0/api-contracts/contracts/zero-tea import { and, eq, or } from "drizzle-orm"; import { convert } from "html-to-text"; +import { env } from "../../lib/env"; import { logger } from "../../lib/log"; import { nowDate } from "../../lib/time"; import { writeDb$, type Db } from "../external/db"; @@ -32,11 +33,18 @@ import { teamsOrgCallbackPayloadSchema, type TeamsOrgCallbackPayload, } from "./teams-org-callback-payload"; -import { buildTeamsConnectUrlForActivity } from "./zero-teams-connect.service"; +import { + buildTeamsConnectUrlForActivity, + disconnectTeamsConnection$, + publishTeamsChanged$, +} from "./zero-teams-connect.service"; import { createZeroRun$ } from "./zero-runs-create.service"; const L = logger("TeamsDispatch"); -const TEAMS_LOGIN_PROMPT_FALLBACK_TEXT = "Please connect your account first"; +const TEAMS_LOGIN_PROMPT_FALLBACK_TEXT = + "Please connect your account to use Zero in this Teams workspace."; + +type TeamsBotCommand = "help" | "connect" | "disconnect" | "switch" | "model"; type TeamsInstallation = typeof teamsOrgInstallations.$inferSelect; type BoundTeamsInstallation = TeamsInstallation & { readonly orgId: string }; @@ -68,6 +76,11 @@ type EffectiveComposeResolution = readonly status: "not_configured" | "not_found" | "not_accessible"; }; +type ResolvedEffectiveCompose = Extract< + EffectiveComposeResolution, + { readonly status: "resolved" } +>; + type TeamsMessageDispatchResult = | { readonly kind: "ignored" } | { @@ -101,6 +114,98 @@ function optionalLine( return normalized ? [`${label}: ${normalized}`] : []; } +function worksUrl(): string { + return `${env("APP_URL")}/works`; +} + +function isTeamsBotCommand(value: string): value is TeamsBotCommand { + return ( + value === "help" || + value === "connect" || + value === "disconnect" || + value === "switch" || + value === "model" + ); +} + +function parseTeamsBotCommand(prompt: string): TeamsBotCommand | null { + const parts = prompt.trim().split(/\s+/u); + const first = parts[0]?.toLowerCase() ?? ""; + const prefixed = first === "/zero" || first === "zero"; + const command = prefixed ? (parts[1]?.toLowerCase() ?? "") : first; + if (!isTeamsBotCommand(command)) { + return null; + } + return prefixed || parts.length === 1 ? command : null; +} + +function commandHelpNotice(args: { + readonly canSwitch: boolean; + readonly canModel: boolean; +}): TeamsMessageDispatchResult { + const switchLine = args.canSwitch + ? "\n- `switch` - Choose which agent responds to your messages" + : ""; + const modelLine = args.canModel ? "\n- `model` - Choose your model" : ""; + return { + kind: "notice", + replyText: [ + "**Zero Teams Bot Help**", + "", + "**Commands**", + `- \`connect\` - Connect to Zero${switchLine}${modelLine}`, + "- `disconnect` - Disconnect from Zero", + "", + "**Usage**", + "- `@Zero ` - Send a message to your agent", + "- Send a DM to Zero to chat without mentioning the bot", + ].join("\n"), + }; +} + +function connectedNotice(): TeamsMessageDispatchResult { + return { + kind: "notice", + replyText: + "You're already connected. Mention @Zero in any channel or send a DM to start chatting with your agent.", + }; +} + +function notInstalledNotice(): TeamsMessageDispatchResult { + return { + kind: "notice", + replyText: + "The Zero Teams app hasn't been set up for this workspace yet. An org admin can complete the setup from VM0.", + }; +} + +function disconnectedNotice(): TeamsMessageDispatchResult { + return { + kind: "notice", + replyText: + "You have been disconnected and your agent access has been revoked.", + }; +} + +function switchNotice( + agent: TeamsAgent | undefined, +): TeamsMessageDispatchResult { + const current = agent + ? `\n\nCurrent agent: \`${agent.displayName ?? agent.name}\`` + : ""; + return { + kind: "notice", + replyText: `Choose which agent responds to your Teams messages from [Works](${worksUrl()}).${current}`, + }; +} + +function modelNotice(): TeamsMessageDispatchResult { + return { + kind: "notice", + replyText: `Choose the model for your Teams agent from [Works](${worksUrl()}).`, + }; +} + async function installationForTenant( db: Db, tenantId: string, @@ -850,6 +955,169 @@ function composeResolutionNotice( } } +function unboundInstallationNotice(args: { + readonly command: TeamsBotCommand | null; + readonly activity: TeamsMessageActivity; + readonly installation: TeamsInstallation | null; +}): TeamsMessageDispatchResult { + if (args.command === "help") { + return commandHelpNotice({ canSwitch: false, canModel: false }); + } + if (args.command === "connect" && !args.installation) { + return notInstalledNotice(); + } + return connectNotice(args.activity, args.installation); +} + +function missingConnectionNotice(args: { + readonly command: TeamsBotCommand | null; + readonly activity: TeamsMessageActivity; + readonly installation: TeamsInstallation; +}): TeamsMessageDispatchResult { + if (args.command === "help") { + return commandHelpNotice({ canSwitch: true, canModel: false }); + } + return connectNotice(args.activity, args.installation); +} + +const connectedCommandBeforeCompose$ = command( + async ( + { set }, + args: { + readonly command: TeamsBotCommand | null; + readonly installation: BoundTeamsInstallation; + readonly connection: TeamsConnection; + }, + signal: AbortSignal, + ): Promise => { + switch (args.command) { + case "help": { + return commandHelpNotice({ canSwitch: true, canModel: true }); + } + case "connect": { + return connectedNotice(); + } + case "disconnect": { + const result = await set( + disconnectTeamsConnection$, + { + orgId: args.installation.orgId, + userId: args.connection.vm0UserId, + }, + signal, + ); + signal.throwIfAborted(); + if (result.kind === "not_found") { + return { + kind: "notice", + replyText: "You are not connected.", + }; + } + await set( + publishTeamsChanged$, + { orgId: result.orgId, userIds: [result.userId] }, + signal, + ); + signal.throwIfAborted(); + return disconnectedNotice(); + } + case "switch": + case "model": + case null: { + return null; + } + } + }, +); + +function composeCommandNotice(args: { + readonly command: TeamsBotCommand | null; + readonly effectiveCompose: EffectiveComposeResolution; +}): TeamsMessageDispatchResult | null { + if (args.command === "switch") { + return switchNotice( + args.effectiveCompose.status === "resolved" + ? args.effectiveCompose.agent + : undefined, + ); + } + if (args.command === "model") { + return modelNotice(); + } + return null; +} + +const runResolvedTeamsAgentForActivity$ = command( + async ( + { set }, + args: { + readonly db: Db; + readonly prompt: string; + readonly activity: TeamsMessageActivity; + readonly installation: BoundTeamsInstallation; + readonly connection: TeamsConnection; + readonly effectiveCompose: ResolvedEffectiveCompose; + readonly apiStartTime: number; + }, + signal: AbortSignal, + ): Promise => { + const modelRoute = await set( + resolveIntegrationModelRouteForUser$, + { + orgId: args.installation.orgId, + userId: args.connection.vm0UserId, + }, + signal, + ); + signal.throwIfAborted(); + + const existingSessionId = await resolveCompatibleTeamsThreadSession({ + db: args.db, + connectionId: args.connection.id, + conversationId: args.activity.conversationId, + threadId: args.activity.threadId, + userId: args.connection.vm0UserId, + composeId: args.effectiveCompose.composeId, + modelRoute, + }); + signal.throwIfAborted(); + + const threadContext = await fetchTeamsPromptContext({ + activity: args.activity, + signal, + }); + signal.throwIfAborted(); + + const result = await set( + runAgentForTeams$, + { + activity: { ...args.activity, text: args.prompt }, + installation: args.installation, + connection: args.connection, + composeId: args.effectiveCompose.composeId, + sessionId: existingSessionId, + threadContext, + apiStartTime: args.apiStartTime, + modelRoute, + }, + signal, + ); + signal.throwIfAborted(); + + if (result.kind === "failed") { + L.warn("Teams agent dispatch failed", { + tenantId: args.activity.tenantId, + conversationId: args.activity.conversationId, + threadId: args.activity.threadId, + userId: args.connection.vm0UserId, + runId: result.runId, + }); + } + + return result; + }, +); + export const dispatchTeamsMessageToAgent$ = command( async ( { set }, @@ -860,11 +1128,11 @@ export const dispatchTeamsMessageToAgent$ = command( }, signal: AbortSignal, ): Promise => { - if (args.activity.kind !== "message") { + const { activity } = args; + if (activity.kind !== "message") { return { kind: "ignored" }; } - const activity = args.activity; if (!shouldDispatchTeamsMessage(activity)) { return { kind: "ignored" }; } @@ -876,6 +1144,7 @@ export const dispatchTeamsMessageToAgent$ = command( replyText: "Please include a message for Zero.", }; } + const command = parseTeamsBotCommand(prompt); const db = set(writeDb$); const installation = @@ -885,7 +1154,7 @@ export const dispatchTeamsMessageToAgent$ = command( signal.throwIfAborted(); if (!installation?.orgId) { - return connectNotice(activity, installation); + return unboundInstallationNotice({ command, activity, installation }); } const boundInstallation: BoundTeamsInstallation = { ...installation, @@ -901,7 +1170,17 @@ export const dispatchTeamsMessageToAgent$ = command( signal.throwIfAborted(); if (!connection) { - return connectNotice(activity, installation); + return missingConnectionNotice({ command, activity, installation }); + } + + const commandResult = await set( + connectedCommandBeforeCompose$, + { command, installation: boundInstallation, connection }, + signal, + ); + signal.throwIfAborted(); + if (commandResult) { + return commandResult; } const effectiveCompose = await resolveEffectiveCompose({ @@ -911,63 +1190,30 @@ export const dispatchTeamsMessageToAgent$ = command( }); signal.throwIfAborted(); + const composeCommandResult = composeCommandNotice({ + command, + effectiveCompose, + }); + if (composeCommandResult) { + return composeCommandResult; + } + if (effectiveCompose.status !== "resolved") { return composeResolutionNotice(effectiveCompose.status); } - const modelRoute = await set( - resolveIntegrationModelRouteForUser$, - { - orgId: boundInstallation.orgId, - userId: connection.vm0UserId, - }, - signal, - ); - signal.throwIfAborted(); - - const existingSessionId = await resolveCompatibleTeamsThreadSession({ - db, - connectionId: connection.id, - conversationId: activity.conversationId, - threadId: activity.threadId, - userId: connection.vm0UserId, - composeId: effectiveCompose.composeId, - modelRoute, - }); - signal.throwIfAborted(); - - const threadContext = await fetchTeamsPromptContext({ - activity, - signal, - }); - signal.throwIfAborted(); - - const result = await set( - runAgentForTeams$, + return set( + runResolvedTeamsAgentForActivity$, { - activity: { ...activity, text: prompt }, + db, + prompt, + activity, installation: boundInstallation, connection, - composeId: effectiveCompose.composeId, - sessionId: existingSessionId, - threadContext, + effectiveCompose, apiStartTime: args.apiStartTime, - modelRoute, }, signal, ); - signal.throwIfAborted(); - - if (result.kind === "failed") { - L.warn("Teams agent dispatch failed", { - tenantId: activity.tenantId, - conversationId: activity.conversationId, - threadId: activity.threadId, - userId: connection.vm0UserId, - runId: result.runId, - }); - } - - return result; }, ); diff --git a/turbo/apps/platform/src/signals/zero-page/teams-connect-signals.ts b/turbo/apps/platform/src/signals/zero-page/teams-connect-signals.ts index 010820a085c..f7bb4657a84 100644 --- a/turbo/apps/platform/src/signals/zero-page/teams-connect-signals.ts +++ b/turbo/apps/platform/src/signals/zero-page/teams-connect-signals.ts @@ -90,6 +90,7 @@ export const connectTeamsAccount$ = command( const teamName = params.get("teamName"); const serviceUrl = params.get("serviceUrl"); const conversationId = params.get("conversationId"); + const conversationType = params.get("conversationType"); const activityId = params.get("activityId"); const channelId = params.get("channelId"); const threadId = params.get("threadId"); @@ -107,6 +108,7 @@ export const connectTeamsAccount$ = command( ...(teamName ? { teamName } : {}), ...(serviceUrl ? { serviceUrl } : {}), ...(conversationId ? { conversationId } : {}), + ...(conversationType ? { conversationType } : {}), ...(activityId ? { activityId } : {}), ...(channelId ? { channelId } : {}), ...(threadId ? { threadId } : {}), diff --git a/turbo/apps/platform/src/views/zero-page/__tests__/zero-teams-connect-page.test.tsx b/turbo/apps/platform/src/views/zero-page/__tests__/zero-teams-connect-page.test.tsx index 74851c36f0b..b8f8a5a6105 100644 --- a/turbo/apps/platform/src/views/zero-page/__tests__/zero-teams-connect-page.test.tsx +++ b/turbo/apps/platform/src/views/zero-page/__tests__/zero-teams-connect-page.test.tsx @@ -74,6 +74,7 @@ describe("zero Teams connect page", () => { teamsUserPrincipalName: "ada@example.com", teamId: "team-123", teamName: "Core Team", + conversationType: "personal", }); return respond(200, { success: true, @@ -92,6 +93,7 @@ describe("zero Teams connect page", () => { teamsUserPrincipalName: "ada@example.com", teamId: "team-123", teamName: "Core Team", + conversationType: "personal", }), ); diff --git a/turbo/packages/api-contracts/src/contracts/zero-teams-browser-connect.ts b/turbo/packages/api-contracts/src/contracts/zero-teams-browser-connect.ts index c104bd87a76..7d746bbc0a1 100644 --- a/turbo/packages/api-contracts/src/contracts/zero-teams-browser-connect.ts +++ b/turbo/packages/api-contracts/src/contracts/zero-teams-browser-connect.ts @@ -17,6 +17,7 @@ export const zeroTeamsBrowserConnectQuerySchema = z.object({ teamName: z.string().optional(), serviceUrl: z.string().optional(), conversationId: z.string().optional(), + conversationType: z.string().optional(), activityId: z.string().optional(), channelId: z.string().optional(), threadId: z.string().optional(), diff --git a/turbo/packages/api-contracts/src/contracts/zero-teams-connect.ts b/turbo/packages/api-contracts/src/contracts/zero-teams-connect.ts index 0eabacdf3f1..c59df1b5577 100644 --- a/turbo/packages/api-contracts/src/contracts/zero-teams-connect.ts +++ b/turbo/packages/api-contracts/src/contracts/zero-teams-connect.ts @@ -7,6 +7,13 @@ const c = initContract(); const nullableStringSchema = z.string().nullable(); +const teamsEnvironmentSchema = z.object({ + requiredSecrets: z.array(z.string()), + requiredVars: z.array(z.string()), + missingSecrets: z.array(z.string()), + missingVars: z.array(z.string()), +}); + const teamsConnectStatusSchema = z.object({ isInstalled: z.boolean(), isConnected: z.boolean(), @@ -18,6 +25,8 @@ const teamsConnectStatusSchema = z.object({ teamId: nullableStringSchema.optional(), teamName: nullableStringSchema.optional(), defaultAgentName: nullableStringSchema.optional(), + agentOrgSlug: nullableStringSchema.optional(), + environment: teamsEnvironmentSchema.optional(), }); const teamsConnectBodySchema = z @@ -32,6 +41,7 @@ const teamsConnectBodySchema = z teamName: z.string().min(1).optional(), serviceUrl: z.string().min(1).optional(), conversationId: z.string().min(1).optional(), + conversationType: z.string().min(1).optional(), activityId: z.string().min(1).optional(), channelId: z.string().min(1).optional(), threadId: z.string().min(1).optional(), From b5fe90b8ae4f21e169752fece1cc225a5a3302e4 Mon Sep 17 00:00:00 2001 From: Linghan Hu Date: Thu, 9 Jul 2026 09:40:43 +0000 Subject: [PATCH 2/3] feat: complete teams slack parity workflows --- turbo/apps/api/src/lib/teams-bot-activity.ts | 2 + .../src/signals/external/teams-bot-client.ts | 134 +- .../__tests__/helpers/zero-teams-connect.ts | 7 +- .../internal-callbacks-teams.test.ts | 273 +- .../routes/__tests__/zero-teams-bot.test.ts | 664 +- .../__tests__/zero-teams-connect.test.ts | 15 +- .../signals/routes/test-computer-use-state.ts | 389 +- .../api/src/signals/routes/zero-teams-bot.ts | 231 +- .../services/agent-run-callbacks.service.ts | 40 +- .../services/api-dispatch-timing.service.ts | 2 + ...internal-teams-org-run-callback.service.ts | 107 +- ...zero-computer-use-authorization.service.ts | 308 +- .../services/zero-computer-use.service.ts | 5 + .../services/zero-teams-connect.service.ts | 27 +- .../services/zero-teams-dispatch.service.ts | 770 +- .../computer-use-authorization-page.test.tsx | 33 + .../computer-use-authorization-page.tsx | 16 +- .../src/contracts/test-computer-use-state.ts | 11 +- .../src/contracts/zero-computer-use.ts | 6 +- .../src/contracts/zero-teams-bot.ts | 2 + .../src/migrations/0565_eager_iron_monger.sql | 38 + .../db/src/migrations/meta/0565_snapshot.json | 19827 ++++++++++++++++ .../db/src/migrations/meta/_journal.json | 9 +- .../db/src/schema/computer-use-host.ts | 20 +- .../db/src/schema/teams-org-thread-session.ts | 10 + 25 files changed, 22479 insertions(+), 467 deletions(-) create mode 100644 turbo/packages/db/src/migrations/0565_eager_iron_monger.sql create mode 100644 turbo/packages/db/src/migrations/meta/0565_snapshot.json diff --git a/turbo/apps/api/src/lib/teams-bot-activity.ts b/turbo/apps/api/src/lib/teams-bot-activity.ts index dbfed65f761..783b7cefb09 100644 --- a/turbo/apps/api/src/lib/teams-bot-activity.ts +++ b/turbo/apps/api/src/lib/teams-bot-activity.ts @@ -240,6 +240,7 @@ function messageActivity( recipientId: recipient?.id ?? null, recipientName: recipient?.name ?? null, }); + const value = isRecord(activity.value) ? activity.value : null; return { ...base, @@ -249,6 +250,7 @@ function messageActivity( recipient, rawText, text: mentionNormalization.text, + value, mentionsRecipient: mentionNormalization.mentionsRecipient, }; } diff --git a/turbo/apps/api/src/signals/external/teams-bot-client.ts b/turbo/apps/api/src/signals/external/teams-bot-client.ts index e32e504e02c..f440d5bbe42 100644 --- a/turbo/apps/api/src/signals/external/teams-bot-client.ts +++ b/turbo/apps/api/src/signals/external/teams-bot-client.ts @@ -73,6 +73,8 @@ type SendTeamsActivityResult = | { readonly kind: "ok"; readonly activityId: string | undefined } | TeamsApiErrorResult; +type SendTeamsReactionResult = { readonly kind: "ok" } | TeamsApiErrorResult; + type CreateTeamsConversationResult = | { readonly kind: "ok"; readonly conversationId: string } | TeamsApiErrorResult; @@ -98,17 +100,46 @@ interface TeamsAdaptiveCardTextBlock { readonly wrap?: boolean; } +interface TeamsAdaptiveCardChoice { + readonly title: string; + readonly value: string; +} + +interface TeamsAdaptiveCardChoiceSetInput { + readonly type: "Input.ChoiceSet"; + readonly id: string; + readonly label?: string; + readonly style?: "compact" | "expanded"; + readonly isMultiSelect?: false; + readonly value?: string; + readonly choices: readonly TeamsAdaptiveCardChoice[]; +} + +type TeamsAdaptiveCardBodyElement = + | TeamsAdaptiveCardTextBlock + | TeamsAdaptiveCardChoiceSetInput; + interface TeamsAdaptiveCardOpenUrlAction { readonly type: "Action.OpenUrl"; readonly title: string; readonly url: string; } +interface TeamsAdaptiveCardSubmitAction { + readonly type: "Action.Submit"; + readonly title: string; + readonly data?: Readonly>; +} + +type TeamsAdaptiveCardAction = + | TeamsAdaptiveCardOpenUrlAction + | TeamsAdaptiveCardSubmitAction; + export interface TeamsAdaptiveCard { readonly type: "AdaptiveCard"; readonly version: "1.4"; - readonly body: readonly TeamsAdaptiveCardTextBlock[]; - readonly actions?: readonly TeamsAdaptiveCardOpenUrlAction[]; + readonly body: readonly TeamsAdaptiveCardBodyElement[]; + readonly actions?: readonly TeamsAdaptiveCardAction[]; } interface TeamsActivityAttachment { @@ -280,6 +311,23 @@ function teamsConversationActivityUrl(args: { : base; } +function teamsConversationReactionUrl(args: { + readonly serviceUrl: string; + readonly conversationId: string; + readonly activityId: string; + readonly reactionType: string; +}): string | undefined { + const activityUrl = teamsConversationActivityUrl({ + serviceUrl: args.serviceUrl, + conversationId: args.conversationId, + activityId: args.activityId, + }); + if (!activityUrl) { + return undefined; + } + return `${activityUrl}/reactions/${encodeURIComponent(args.reactionType)}`; +} + function teamsConversationsUrl(serviceUrl: string): string | undefined { const parsed = safeUrlParse(serviceUrl); if ( @@ -500,6 +548,60 @@ export async function createTeamsPersonalConversation(args: { return { kind: "ok", conversationId: parsed.data.id }; } +async function requestTeamsReaction(args: { + readonly method: "PUT" | "DELETE"; + readonly serviceUrl: string; + readonly conversationId: string; + readonly activityId: string; + readonly tenantId: string; + readonly reactionType: string; + readonly signal: AbortSignal; +}): Promise { + const accessToken = await fetchTeamsBotAccessToken({ + tenantId: args.tenantId, + signal: args.signal, + }); + if (accessToken.kind === "teams-error") { + return accessToken; + } + + const url = teamsConversationReactionUrl({ + serviceUrl: args.serviceUrl, + conversationId: args.conversationId, + activityId: args.activityId, + reactionType: args.reactionType, + }); + if (!url) { + return teamsApiError(400, "Invalid Microsoft Teams serviceUrl"); + } + + const responseResult = await settle( + fetch(url, { + method: args.method, + headers: { + authorization: `Bearer ${accessToken.accessToken}`, + }, + signal: args.signal, + }), + args.signal, + ); + if (!responseResult.ok) { + return teamsApiError(502, networkErrorMessage(responseResult.error)); + } + + const response = responseResult.value; + const responseText = await response.text(); + args.signal.throwIfAborted(); + if (!response.ok) { + return teamsApiError( + response.status, + responseText || `Microsoft Teams API returned HTTP ${response.status}`, + ); + } + + return { kind: "ok" }; +} + export function sendTeamsMessageReply(args: { readonly serviceUrl: string; readonly conversationId: string; @@ -538,6 +640,34 @@ export function sendTeamsMessageReply(args: { }); } +export function sendTeamsReaction(args: { + readonly serviceUrl: string; + readonly conversationId: string; + readonly activityId: string; + readonly tenantId: string; + readonly reactionType: string; + readonly signal: AbortSignal; +}): Promise { + return requestTeamsReaction({ + method: "PUT", + ...args, + }); +} + +export function deleteTeamsReaction(args: { + readonly serviceUrl: string; + readonly conversationId: string; + readonly activityId: string; + readonly tenantId: string; + readonly reactionType: string; + readonly signal: AbortSignal; +}): Promise { + return requestTeamsReaction({ + method: "DELETE", + ...args, + }); +} + export function sendTeamsTypingActivity(args: { readonly serviceUrl: string; readonly conversationId: string; diff --git a/turbo/apps/api/src/signals/routes/__tests__/helpers/zero-teams-connect.ts b/turbo/apps/api/src/signals/routes/__tests__/helpers/zero-teams-connect.ts index 6561031af02..f79e821b530 100644 --- a/turbo/apps/api/src/signals/routes/__tests__/helpers/zero-teams-connect.ts +++ b/turbo/apps/api/src/signals/routes/__tests__/helpers/zero-teams-connect.ts @@ -221,7 +221,12 @@ export async function installTeamsForTest( ): Promise { const response = await postTeamsActivityForTest({ signal, - activity: teamsMessageActivityForTest(fixture), + activity: teamsMessageActivityForTest(fixture, { + id: "activity-install-seed", + text: "installation seed", + entities: [], + replyToId: null, + }), }); if (!response.ok) { throw new Error(`Teams install seed failed with ${response.status}`); diff --git a/turbo/apps/api/src/signals/routes/__tests__/internal-callbacks-teams.test.ts b/turbo/apps/api/src/signals/routes/__tests__/internal-callbacks-teams.test.ts index ccdcaf1e973..d738dddbcb0 100644 --- a/turbo/apps/api/src/signals/routes/__tests__/internal-callbacks-teams.test.ts +++ b/turbo/apps/api/src/signals/routes/__tests__/internal-callbacks-teams.test.ts @@ -64,8 +64,16 @@ interface TeamsPostedActivity { readonly channelData?: unknown; } +interface TeamsReactionRequest { + readonly method: "PUT" | "DELETE"; + readonly conversationId: string; + readonly activityId: string; + readonly reactionType: string; +} + interface ConnectedTeamsActor { readonly fixture: TeamsConnectFixture; + readonly actor: ReturnType; readonly runnerGroup: string; } @@ -97,9 +105,11 @@ function teamsApiMocks(args: { }): { readonly tokenRequests: URLSearchParams[]; readonly postedActivities: TeamsPostedActivity[]; + readonly reactionRequests: TeamsReactionRequest[]; } { const tokenRequests: URLSearchParams[] = []; const postedActivities: TeamsPostedActivity[] = []; + const reactionRequests: TeamsReactionRequest[] = []; const serviceBaseUrl = teamsServiceBaseUrl(args.serviceUrl); server.use( @@ -154,9 +164,112 @@ function teamsApiMocks(args: { }); }, ), + http.put( + `${serviceBaseUrl}/v3/conversations/:conversationId/activities/:activityId/reactions/:reactionType`, + ({ params }) => { + reactionRequests.push({ + method: "PUT", + conversationId: + typeof params.conversationId === "string" + ? params.conversationId + : "", + activityId: + typeof params.activityId === "string" ? params.activityId : "", + reactionType: + typeof params.reactionType === "string" ? params.reactionType : "", + }); + if (args.activityStatus) { + return new HttpResponse(args.activityError ?? "Teams API failed", { + status: args.activityStatus, + }); + } + return new HttpResponse(null, { status: 200 }); + }, + ), + http.delete( + `${serviceBaseUrl}/v3/conversations/:conversationId/activities/:activityId/reactions/:reactionType`, + ({ params }) => { + reactionRequests.push({ + method: "DELETE", + conversationId: + typeof params.conversationId === "string" + ? params.conversationId + : "", + activityId: + typeof params.activityId === "string" ? params.activityId : "", + reactionType: + typeof params.reactionType === "string" ? params.reactionType : "", + }); + if (args.activityStatus) { + return new HttpResponse(args.activityError ?? "Teams API failed", { + status: args.activityStatus, + }); + } + return new HttpResponse(null, { status: 200 }); + }, + ), + http.get( + "https://graph.microsoft.com/v1.0/teams/:teamId/channels/:channelId/messages", + () => { + return HttpResponse.json({ value: [] }); + }, + ), + http.get( + "https://graph.microsoft.com/v1.0/teams/:teamId/channels/:channelId/messages/:messageId/replies", + () => { + return HttpResponse.json({ value: [] }); + }, + ), + http.get( + "https://graph.microsoft.com/v1.0/teams/:teamId/channels/:channelId/messages/:messageId", + ({ params }) => { + const messageId = + typeof params.messageId === "string" ? params.messageId : "root"; + return HttpResponse.json({ + id: messageId, + createdDateTime: "2026-06-30T09:00:00.000Z", + messageType: "message", + from: { + user: { + id: "29:user-1", + displayName: "Ada Lovelace", + }, + }, + body: { + contentType: "html", + content: "

Teams root context

", + }, + }); + }, + ), ); - return { tokenRequests, postedActivities }; + return { tokenRequests, postedActivities, reactionRequests }; +} + +function clearTeamsApiCalls(api: ReturnType): void { + api.tokenRequests.splice(0, api.tokenRequests.length); + api.postedActivities.splice(0, api.postedActivities.length); + api.reactionRequests.splice(0, api.reactionRequests.length); +} + +function dropLastTeamsIndicatorRequest( + api: ReturnType, +): void { + const lastActivity = api.postedActivities[api.postedActivities.length - 1]; + if (lastActivity?.type === "typing") { + api.postedActivities.pop(); + api.tokenRequests.pop(); + return; + } + + const lastReaction = api.reactionRequests.at(-1); + expect(lastReaction).toMatchObject({ + method: "PUT", + reactionType: "1f4ad_thoughtballoon", + }); + api.reactionRequests.pop(); + api.tokenRequests.pop(); } function mockOpenRouterSummary(summary: string): unknown[] { @@ -175,26 +288,26 @@ function mockOpenRouterSummary(summary: string): unknown[] { return requests; } -function dispatchRunId(body: unknown): string { - const response = recordFromUnknown( - body, - "Expected Teams bot response object", - ); - const dispatch = recordFromUnknown( - response.dispatch, - "Expected Teams dispatch object", - ); - expect( - dispatch.kind === "accepted" || dispatch.kind === "queued", - ).toBeTruthy(); - if (typeof dispatch.runId !== "string") { - throw new Error("Expected Teams dispatch run id"); +async function runIdForPrompt( + actor: ReturnType, + prompt: string, +): Promise { + const list = await runsApi.listAgentRuns(actor, { limit: 20 }); + const run = list.runs.find((item) => { + return item.prompt === prompt; + }); + if (!run) { + throw new Error(`Expected Teams run for prompt: ${prompt}`); } - return dispatch.runId; + return run.id; } async function connectTeamsFixture( fixture: TeamsConnectFixture, + options: { + readonly displayName?: string; + readonly principalName?: string; + } = {}, ): Promise { mocks.clerk.session(fixture.userId, fixture.orgId, "org:admin"); const client = setupApp({ @@ -207,8 +320,8 @@ async function connectTeamsFixture( body: { tenantId: fixture.teamsTenantId, teamsUserId: fixture.teamsUserId, - teamsUserDisplayName: "Ada Lovelace", - teamsUserPrincipalName: "ada@example.com", + teamsUserDisplayName: options.displayName ?? "Ada Lovelace", + teamsUserPrincipalName: options.principalName ?? "ada@example.com", teamId: fixture.teamsTeamId, teamName: fixture.teamsTeamName, serviceUrl: fixture.serviceUrl, @@ -255,10 +368,14 @@ async function setupConnectedTeamsActor( }, ); } + const setupTeamsApi = teamsApiMocks({ serviceUrl: fixture.serviceUrl }); await installTeamsForTest(context.signal, fixture); + await flushWaitUntilForTest(); await connectTeamsFixture(fixture); + await flushWaitUntilForTest(); + clearTeamsApiCalls(setupTeamsApi); - return { fixture, runnerGroup }; + return { fixture, actor, runnerGroup }; } async function dispatchTeamsRun(args: { @@ -266,6 +383,8 @@ async function dispatchTeamsRun(args: { readonly activityId: string; readonly threadId: string; readonly text: string; + readonly senderName?: string; + readonly senderPrincipalName?: string; }): Promise { const response = await postTeamsActivityForTest({ signal: context.signal, @@ -273,10 +392,27 @@ async function dispatchTeamsRun(args: { id: args.activityId, replyToId: args.threadId, text: `Zero ${args.text}`, + from: { + id: args.fixture.teamsUserId, + name: args.senderName ?? "Ada Lovelace", + aadObjectId: args.fixture.teamsAadObjectId, + userPrincipalName: args.senderPrincipalName ?? "ada@example.com", + }, }), }); expect(response.status).toBe(200); - return dispatchRunId(await response.json()); + const body = recordFromUnknown( + await response.json(), + "Expected Teams bot response object", + ); + expect(body).not.toHaveProperty("dispatch"); + await flushWaitUntilForTest(); + const actor = authOrgApi.user({ + userId: args.fixture.userId, + orgId: args.fixture.orgId, + orgRole: "org:admin", + }); + return await runIdForPrompt(actor, args.text); } async function claimTeamsRun(args: { @@ -373,7 +509,8 @@ beforeEach(() => { context.mocks.axiom.query.mockResolvedValue([]); }); -afterEach(() => { +afterEach(async () => { + await flushWaitUntilForTest(); context.mocks.axiom.query.mockReset(); }); @@ -393,6 +530,18 @@ describe("Teams org internal callbacks", () => { runnerGroup: teams.runnerGroup, runId, }); + clearTeamsApiCalls(teamsApi); + + await webhooksApi.requestAgentHeartbeat( + { runId }, + { authorization: `Bearer ${claim.sandboxToken}` }, + [200], + ); + await flushWaitUntilForTest(); + expect(teamsApi.tokenRequests).toHaveLength(0); + expect(teamsApi.postedActivities).toHaveLength(0); + expect(teamsApi.reactionRequests).toHaveLength(0); + clearTeamsApiCalls(teamsApi); const cliAgentSessionId = await completeSandboxRun({ runId, @@ -400,7 +549,7 @@ describe("Teams org internal callbacks", () => { exitCode: 0, }); - expect(teamsApi.tokenRequests).toHaveLength(1); + expect(teamsApi.tokenRequests).toHaveLength(2); expect(teamsApi.tokenRequests[0]?.get("client_id")).toBe(BOT_APP_ID); expect(teamsApi.tokenRequests[0]?.get("client_secret")).toBe( BOT_APP_PASSWORD, @@ -418,11 +567,17 @@ describe("Teams org internal callbacks", () => { "Task completed successfully.", ); expect(teamsApi.postedActivities[0]?.text).toContain( - `[View run details](${APP_URL}/activities/${runId})`, - ); - expect(teamsApi.postedActivities[0]?.text).toContain( - "Reply to Ada Lovelace", + `[Audit](${APP_URL}/activities/${runId})`, ); + expect(teamsApi.postedActivities[0]?.text).not.toContain("Reply to"); + expect(teamsApi.reactionRequests).toStrictEqual([ + { + method: "DELETE", + conversationId: "19:thread@thread.tacv2", + activityId: "activity-completed-1", + reactionType: "1f4ad_thoughtballoon", + }, + ]); expect(summaryRequests).toHaveLength(1); const summaryRequest = recordFromUnknown( summaryRequests[0], @@ -435,6 +590,56 @@ describe("Teams org internal callbacks", () => { "finish the task", ); + const secondFixture = await trackTeamsFixture( + Promise.resolve( + teamsConnectFixture({ + orgId: teams.fixture.orgId, + teamsTenantId: teams.fixture.teamsTenantId, + teamsTenantName: teams.fixture.teamsTenantName, + teamsTeamId: teams.fixture.teamsTeamId, + teamsTeamName: teams.fixture.teamsTeamName, + serviceUrl: teams.fixture.serviceUrl, + }), + ), + ); + authOrgApi.user({ + userId: secondFixture.userId, + orgId: secondFixture.orgId, + orgRole: "org:admin", + }); + const tokenRequestCountBeforeConnect = teamsApi.tokenRequests.length; + const postedActivityCountBeforeConnect = teamsApi.postedActivities.length; + await connectTeamsFixture(secondFixture, { + displayName: "Grace Hopper", + principalName: "grace@example.com", + }); + await flushWaitUntilForTest(); + teamsApi.tokenRequests.splice(tokenRequestCountBeforeConnect); + teamsApi.postedActivities.splice(postedActivityCountBeforeConnect); + const secondRunId = await dispatchTeamsRun({ + fixture: secondFixture, + activityId: "activity-completed-2", + threadId: "root-completed", + text: "finish the follow-up", + senderName: "Grace Hopper", + senderPrincipalName: "grace@example.com", + }); + const secondClaim = await claimTeamsRun({ + runnerGroup: teams.runnerGroup, + runId: secondRunId, + }); + dropLastTeamsIndicatorRequest(teamsApi); + await completeSandboxRun({ + runId: secondRunId, + sandboxToken: secondClaim.sandboxToken, + exitCode: 0, + }); + expect(teamsApi.postedActivities).toHaveLength(2); + expect(teamsApi.postedActivities[1]?.text).toContain( + "Reply to Grace Hopper", + ); + expect(summaryRequests).toHaveLength(2); + const followUpClaim = await claimFollowUpInThread({ fixture: teams.fixture, runnerGroup: teams.runnerGroup, @@ -457,6 +662,7 @@ describe("Teams org internal callbacks", () => { runnerGroup: teams.runnerGroup, runId, }); + clearTeamsApiCalls(teamsApi); await completeSandboxRun({ runId, @@ -469,6 +675,14 @@ describe("Teams org internal callbacks", () => { expect(teamsApi.postedActivities[0]?.text).toContain( "Cannot continue session from checkpoint", ); + expect(teamsApi.reactionRequests).toStrictEqual([ + { + method: "DELETE", + conversationId: "19:thread@thread.tacv2", + activityId: "activity-failed-1", + reactionType: "1f4ad_thoughtballoon", + }, + ]); const followUpClaim = await claimFollowUpInThread({ fixture: teams.fixture, @@ -492,6 +706,7 @@ describe("Teams org internal callbacks", () => { runnerGroup: teams.runnerGroup, runId, }); + clearTeamsApiCalls(teamsApi); await removeTeamsForTest(context.signal, teams.fixture); await completeSandboxRun({ @@ -502,8 +717,12 @@ describe("Teams org internal callbacks", () => { expect(teamsApi.tokenRequests).toHaveLength(0); expect(teamsApi.postedActivities).toHaveLength(0); + expect(teamsApi.reactionRequests).toHaveLength(0); await installTeamsForTest(context.signal, teams.fixture); + await flushWaitUntilForTest(); await connectTeamsFixture(teams.fixture); + await flushWaitUntilForTest(); + clearTeamsApiCalls(teamsApi); const followUpClaim = await claimFollowUpInThread({ fixture: teams.fixture, @@ -531,6 +750,7 @@ describe("Teams org internal callbacks", () => { runnerGroup: teams.runnerGroup, runId, }); + clearTeamsApiCalls(teamsApi); await completeSandboxRun({ runId, @@ -543,6 +763,7 @@ describe("Teams org internal callbacks", () => { expect(teamsApi.postedActivities[0]?.text).toContain( "Task completed successfully.", ); + expect(teamsApi.reactionRequests).toHaveLength(0); const followUpClaim = await claimFollowUpInThread({ fixture: teams.fixture, diff --git a/turbo/apps/api/src/signals/routes/__tests__/zero-teams-bot.test.ts b/turbo/apps/api/src/signals/routes/__tests__/zero-teams-bot.test.ts index 9c46b31af08..5799bfc09c0 100644 --- a/turbo/apps/api/src/signals/routes/__tests__/zero-teams-bot.test.ts +++ b/turbo/apps/api/src/signals/routes/__tests__/zero-teams-bot.test.ts @@ -1,17 +1,22 @@ import { createSign, generateKeyPairSync, type KeyObject } from "node:crypto"; import { zeroTeamsConnectContract } from "@vm0/api-contracts/contracts/zero-teams-connect"; +import { FeatureSwitchKey } from "@vm0/connectors/feature-switch-key"; import { HttpResponse, http } from "msw"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { createAppWithRoutes } from "../../../app-factory-core"; +import { verifyZeroToken } from "../../auth/tokens"; +import { flushWaitUntilForTest } from "../../context/wait-until"; import { mockEnv, mockOptionalEnv } from "../../../lib/env"; import { now } from "../../../lib/time"; import { server } from "../../../mocks/server"; import { accept, setupApp, testContext } from "../../../__tests__/test-helpers"; import { zeroTeamsBotRoutes } from "../zero-teams-bot"; import { createAuthOrgAgentsBddApi } from "./helpers/api-bdd-auth-org"; +import { createComputerUseBddApi } from "./helpers/api-bdd-computer-use"; import { createRunsAutomationsApi } from "./helpers/api-bdd-runs-automations"; +import { createUserConfigBddApi } from "./helpers/api-bdd-user-config"; import { removeTeamsForTest, setupTeamsConnectTestEnv, @@ -22,11 +27,14 @@ import { createFixtureTracker, createZeroRouteMocks, } from "./helpers/zero-route-test"; +import { updateFeatureSwitchesForUser } from "./helpers/zero-feature-switches"; const context = testContext(); const mocks = createZeroRouteMocks(context); const authOrgApi = createAuthOrgAgentsBddApi(context); +const computerUseApi = createComputerUseBddApi(context); const runsApi = createRunsAutomationsApi(context); +const userConfigApi = createUserConfigBddApi(context); const trackTeamsFixture = createFixtureTracker( async (fixture) => { await removeTeamsForTest(context.signal, fixture); @@ -114,12 +122,26 @@ interface TeamsOutboundRequest { readonly body: unknown; } +interface TeamsReactionRequest { + readonly method: "PUT" | "DELETE"; + readonly conversationId: string; + readonly activityId: string; + readonly reactionType: string; +} + +type TeamsOutboundRequests = TeamsOutboundRequest[] & { + readonly reactions: TeamsReactionRequest[]; +}; + function teamsOutboundHandlers( serviceUrl: string, tenantId = "tenant-1", -): TeamsOutboundRequest[] { +): TeamsOutboundRequests { const serviceBaseUrl = teamsServiceBaseUrl(serviceUrl); - const requests: TeamsOutboundRequest[] = []; + const requests: TeamsOutboundRequests = Object.assign( + [] as TeamsOutboundRequest[], + { reactions: [] as TeamsReactionRequest[] }, + ); server.use( http.post(graphTokenUrl(tenantId), async ({ request }) => { const form = await request.formData(); @@ -161,6 +183,40 @@ function teamsOutboundHandlers( return HttpResponse.json({ id: "teams-activity-1" }); }, ), + http.put( + `${serviceBaseUrl}/v3/conversations/:conversationId/activities/:activityId/reactions/:reactionType`, + ({ params }) => { + requests.reactions.push({ + method: "PUT", + conversationId: + typeof params.conversationId === "string" + ? params.conversationId + : "", + activityId: + typeof params.activityId === "string" ? params.activityId : "", + reactionType: + typeof params.reactionType === "string" ? params.reactionType : "", + }); + return new HttpResponse(null, { status: 200 }); + }, + ), + http.delete( + `${serviceBaseUrl}/v3/conversations/:conversationId/activities/:activityId/reactions/:reactionType`, + ({ params }) => { + requests.reactions.push({ + method: "DELETE", + conversationId: + typeof params.conversationId === "string" + ? params.conversationId + : "", + activityId: + typeof params.activityId === "string" ? params.activityId : "", + reactionType: + typeof params.reactionType === "string" ? params.reactionType : "", + }); + return new HttpResponse(null, { status: 200 }); + }, + ), ); return requests; } @@ -213,7 +269,15 @@ function teamsGraphHistoryHandlers(args: { const form = await request.formData(); expect(form.get("client_id")).toBe(BOT_APP_ID); expect(form.get("client_secret")).toBe(BOT_APP_PASSWORD); - expect(form.get("scope")).toBe("https://graph.microsoft.com/.default"); + const scope = form.get("scope"); + if (scope === "https://api.botframework.com/.default") { + return HttpResponse.json({ + access_token: "teams-access-token", + token_type: "Bearer", + expires_in: 3600, + }); + } + expect(scope).toBe("https://graph.microsoft.com/.default"); requests.push("graph-token"); return HttpResponse.json({ access_token: "teams-graph-token", @@ -394,6 +458,38 @@ async function postTeamsActivity(args: { }); } +async function readTeamsBotResponseAndFlush( + response: Response, +): Promise { + expect(response.status).toBe(200); + const body: unknown = await response.json(); + await flushWaitUntilForTest(); + return body; +} + +async function runIdForPrompt( + actor: ReturnType, + prompt: string, +): Promise { + const list = await runsApi.listAgentRuns(actor, { limit: 20 }); + const run = list.runs.find((item) => { + return item.prompt === prompt; + }); + if (!run) { + throw new Error(`Expected Teams run for prompt: ${prompt}`); + } + return run.id; +} + +function requestTokenFromUrl(authorizationUrl: string): string { + const url = new URL(authorizationUrl); + const prefix = "/computer-use/authorize/"; + if (!url.pathname.startsWith(prefix)) { + throw new Error(`Unexpected authorization URL: ${authorizationUrl}`); + } + return decodeURIComponent(url.pathname.slice(prefix.length)); +} + function promptSection( prompt: string, heading: string, @@ -436,6 +532,7 @@ function teamsPersonalMessageActivity(args: { readonly fixture: TeamsConnectFixture; readonly id: string; readonly text: string; + readonly value?: Readonly>; }): Record { return teamsMessageActivity(args.fixture, { id: args.id, @@ -452,15 +549,41 @@ function teamsPersonalMessageActivity(args: { }, text: args.text, entities: [], + ...(args.value ? { value: args.value } : {}), replyToId: null, }); } +function teamsPersonalThreadMessageActivity(args: { + readonly fixture: TeamsConnectFixture; + readonly id: string; + readonly threadId: string; + readonly text: string; +}): Record { + return teamsMessageActivity(args.fixture, { + id: args.id, + conversation: { + id: `a:personal-${args.fixture.teamsUserId}`, + conversationType: "personal", + }, + channelData: { + tenant: { + id: args.fixture.teamsTenantId, + name: args.fixture.teamsTenantName, + }, + teamsAppId: "teams-app-test", + }, + text: args.text, + entities: [], + replyToId: args.threadId, + }); +} + async function setupConnectedTeamsBotActor(): Promise<{ readonly fixture: TeamsConnectFixture; readonly actor: ReturnType; readonly runnerGroup: string; - readonly outboundRequests: TeamsOutboundRequest[]; + readonly outboundRequests: TeamsOutboundRequests; }> { const fixture = await trackTeamsFixture( Promise.resolve(teamsConnectFixture()), @@ -493,24 +616,13 @@ async function setupConnectedTeamsBotActor(): Promise<{ token: teamsToken(), }); expect(installResponse.status).toBe(200); + await installResponse.json(); + await flushWaitUntilForTest(); await connectTeamsFixture(fixture); return { fixture, actor, runnerGroup, outboundRequests }; } -function dispatchRunId(dispatch: unknown): string { - if (typeof dispatch !== "object" || dispatch === null) { - throw new Error("Expected Teams dispatch object"); - } - const kind = Reflect.get(dispatch, "kind"); - expect(kind === "accepted" || kind === "queued").toBeTruthy(); - const runId = Reflect.get(dispatch, "runId"); - if (typeof runId !== "string") { - throw new Error("Expected Teams dispatch run id"); - } - return runId; -} - describe("POST /api/zero/teams/bot", () => { beforeEach(() => { setupTeamsConnectTestEnv(APP_ORIGIN); @@ -523,6 +635,7 @@ describe("POST /api/zero/teams/bot", () => { }); afterEach(async () => { + await flushWaitUntilForTest(); await removeTeamsForTest(context.signal, botFixture()); }); @@ -619,11 +732,8 @@ describe("POST /api/zero/teams/bot", () => { expect(connectUrl.searchParams.get("teamId")).toBe("team-1"); expect(connectUrl.searchParams.get("teamName")).toBe("Team One"); expect(connectUrl.searchParams.get("conversationType")).toBe("channel"); - expect(body.dispatch).toMatchObject({ - kind: "notice", - connectUrl: expect.stringContaining(`${APP_ORIGIN}/settings/teams`), - replyText: TEAMS_LOGIN_PROMPT_FALLBACK_TEXT, - }); + expect(body).not.toHaveProperty("dispatch"); + await flushWaitUntilForTest(); expect(outboundRequests).toHaveLength(1); expect(outboundRequests[0]).toMatchObject({ conversationId: "19:thread@thread.tacv2", @@ -710,14 +820,16 @@ describe("POST /api/zero/teams/bot", () => { }); expect(response.status).toBe(200); - await expect(response.json()).resolves.toMatchObject({ + const body = await response.json(); + expect(body).toMatchObject({ activity: { kind: "message", text: "hello channel", mentionsRecipient: false, }, - dispatch: { kind: "ignored" }, }); + expect(body).not.toHaveProperty("dispatch"); + await flushWaitUntilForTest(); }); it("handles Teams personal messages without requiring a bot mention", async () => { @@ -743,7 +855,8 @@ describe("POST /api/zero/teams/bot", () => { }); expect(response.status).toBe(200); - await expect(response.json()).resolves.toMatchObject({ + const body = await response.json(); + expect(body).toMatchObject({ activity: { kind: "message", conversationType: "personal", @@ -751,11 +864,9 @@ describe("POST /api/zero/teams/bot", () => { text: "hello from dm", mentionsRecipient: false, }, - dispatch: { - kind: "notice", - replyText: TEAMS_LOGIN_PROMPT_FALLBACK_TEXT, - }, }); + expect(body).not.toHaveProperty("dispatch"); + await flushWaitUntilForTest(); expect(outboundRequests).toHaveLength(1); expect(outboundRequests[0]).toMatchObject({ conversationId: "a:personal-conversation", @@ -816,17 +927,25 @@ describe("POST /api/zero/teams/bot", () => { }); expect(response.status).toBe(200); - await expect(response.json()).resolves.toMatchObject({ + const body = await response.json(); + expect(body).toMatchObject({ activity: { kind: "message", text: "ask @Grace Hopper (29:user-2) to review", mentionsRecipient: true, }, }); + expect(body).not.toHaveProperty("dispatch"); + await flushWaitUntilForTest(); }); it("handles connected Teams bot commands", async () => { - const { fixture, outboundRequests } = await setupConnectedTeamsBotActor(); + const { fixture, actor, outboundRequests } = + await setupConnectedTeamsBotActor(); + const switchAgent = await authOrgApi.createAgent(actor, { + displayName: "Teams support agent", + visibility: "public", + }); outboundRequests.splice(0, outboundRequests.length); const helpResponse = await postTeamsActivity({ @@ -838,12 +957,8 @@ describe("POST /api/zero/teams/bot", () => { token: teamsToken(), }); expect(helpResponse.status).toBe(200); - await expect(helpResponse.json()).resolves.toMatchObject({ - dispatch: { - kind: "notice", - replyText: expect.stringContaining("Zero Teams Bot Help"), - }, - }); + const helpBody = await readTeamsBotResponseAndFlush(helpResponse); + expect(helpBody).not.toHaveProperty("dispatch"); const switchResponse = await postTeamsActivity({ activity: teamsPersonalMessageActivity({ @@ -854,12 +969,8 @@ describe("POST /api/zero/teams/bot", () => { token: teamsToken(), }); expect(switchResponse.status).toBe(200); - await expect(switchResponse.json()).resolves.toMatchObject({ - dispatch: { - kind: "notice", - replyText: expect.stringContaining("/works"), - }, - }); + const switchBody = await readTeamsBotResponseAndFlush(switchResponse); + expect(switchBody).not.toHaveProperty("dispatch"); const modelResponse = await postTeamsActivity({ activity: teamsPersonalMessageActivity({ @@ -870,30 +981,64 @@ describe("POST /api/zero/teams/bot", () => { token: teamsToken(), }); expect(modelResponse.status).toBe(200); - await expect(modelResponse.json()).resolves.toMatchObject({ - dispatch: { - kind: "notice", - replyText: expect.stringContaining("Choose the model"), + const modelBody = await readTeamsBotResponseAndFlush(modelResponse); + expect(modelBody).not.toHaveProperty("dispatch"); + + const switchSubmitResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-command-switch-submit", + text: "", + value: { + zeroTeamsAction: "switch_agent", + selectedComposeId: switchAgent.agentId, + }, + }), + token: teamsToken(), + }); + expect(switchSubmitResponse.status).toBe(200); + const switchSubmitBody = + await readTeamsBotResponseAndFlush(switchSubmitResponse); + expect(switchSubmitBody).toMatchObject({ + activity: { + value: { + zeroTeamsAction: "switch_agent", + selectedComposeId: switchAgent.agentId, + }, }, }); - - const disconnectResponse = await postTeamsActivity({ + expect(switchSubmitBody).not.toHaveProperty("dispatch"); + const modelSubmitResponse = await postTeamsActivity({ activity: teamsPersonalMessageActivity({ fixture, - id: "activity-command-disconnect", - text: "disconnect", + id: "activity-command-model-submit", + text: "", + value: { + zeroTeamsAction: "switch_model", + selectedModel: "claude-sonnet-4-6", + }, }), token: teamsToken(), }); - expect(disconnectResponse.status).toBe(200); - await expect(disconnectResponse.json()).resolves.toMatchObject({ - dispatch: { - kind: "notice", - replyText: expect.stringContaining("You have been disconnected"), + expect(modelSubmitResponse.status).toBe(200); + const modelSubmitBody = + await readTeamsBotResponseAndFlush(modelSubmitResponse); + expect(modelSubmitBody).toMatchObject({ + activity: { + value: { + zeroTeamsAction: "switch_model", + selectedModel: "claude-sonnet-4-6", + }, }, }); + expect(modelSubmitBody).not.toHaveProperty("dispatch"); + await expect( + userConfigApi.readModelPreference(actor), + ).resolves.toMatchObject({ + selectedModel: "claude-sonnet-4-6", + }); - expect(outboundRequests).toHaveLength(4); + expect(outboundRequests).toHaveLength(5); expect( outboundRequests.map((request) => { return request.activityId; @@ -902,16 +1047,141 @@ describe("POST /api/zero/teams/bot", () => { "activity-command-help", "activity-command-switch", "activity-command-model", - "activity-command-disconnect", + "activity-command-switch-submit", + "activity-command-model-submit", ]); expect(outboundRequests[0]?.body).toMatchObject({ type: "message", text: expect.stringContaining("Zero Teams Bot Help"), }); + expect(outboundRequests[1]?.body).toMatchObject({ + type: "message", + summary: expect.stringContaining("Choose which agent should respond"), + attachments: [ + { + contentType: "application/vnd.microsoft.card.adaptive", + content: { + type: "AdaptiveCard", + version: "1.4", + body: expect.arrayContaining([ + expect.objectContaining({ + type: "Input.ChoiceSet", + id: "selectedComposeId", + choices: expect.arrayContaining([ + expect.objectContaining({ + title: expect.stringContaining("Use org default"), + value: "__org_default__", + }), + expect.objectContaining({ + title: "Teams support agent", + value: switchAgent.agentId, + }), + ]), + }), + ]), + actions: [ + { + type: "Action.Submit", + title: "Switch", + data: { zeroTeamsAction: "switch_agent" }, + }, + ], + }, + }, + ], + }); + expect(outboundRequests[2]?.body).toMatchObject({ + type: "message", + summary: expect.stringContaining("Choose the model"), + attachments: [ + { + contentType: "application/vnd.microsoft.card.adaptive", + content: { + type: "AdaptiveCard", + version: "1.4", + body: expect.arrayContaining([ + expect.objectContaining({ + type: "Input.ChoiceSet", + id: "selectedModel", + choices: expect.arrayContaining([ + expect.objectContaining({ + title: expect.stringContaining("Claude Sonnet 4.6"), + value: "claude-sonnet-4-6", + }), + ]), + }), + ]), + actions: [ + { + type: "Action.Submit", + title: "Switch", + data: { zeroTeamsAction: "switch_model" }, + }, + ], + }, + }, + ], + }); expect(outboundRequests[3]?.body).toMatchObject({ type: "message", - text: expect.stringContaining("agent access has been revoked"), + text: expect.stringContaining("Teams support agent"), + }); + expect(outboundRequests[4]?.body).toMatchObject({ + type: "message", + text: expect.stringContaining("Claude Sonnet 4.6"), + }); + + outboundRequests.splice(0, outboundRequests.length); + const switchedRunResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-command-switch-run", + text: "run after switch", + }), + token: teamsToken(), + }); + expect(switchedRunResponse.status).toBe(200); + const switchedRunBody = + await readTeamsBotResponseAndFlush(switchedRunResponse); + expect(switchedRunBody).not.toHaveProperty("dispatch"); + const switchedRunId = await runIdForPrompt(actor, "run after switch"); + await expect( + runsApi.listAgentRuns(actor, { limit: 10 }), + ).resolves.toMatchObject({ + runs: expect.arrayContaining([ + expect.objectContaining({ + appendSystemPrompt: expect.stringContaining( + "Your name is Teams support agent.", + ), + prompt: "run after switch", + }), + ]), + }); + await runsApi.requestCancelRun(actor, switchedRunId, [200]); + + outboundRequests.splice(0, outboundRequests.length); + const disconnectResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-command-disconnect", + text: "disconnect", + }), + token: teamsToken(), }); + expect(disconnectResponse.status).toBe(200); + const disconnectBody = + await readTeamsBotResponseAndFlush(disconnectResponse); + expect(disconnectBody).not.toHaveProperty("dispatch"); + expect(outboundRequests).toStrictEqual( + expect.arrayContaining([ + expect.objectContaining({ + body: expect.objectContaining({ + type: "message", + text: expect.stringContaining("agent access has been revoked"), + }), + }), + ]), + ); }); it("replies when a connected Teams run is queued", async () => { @@ -928,7 +1198,9 @@ describe("POST /api/zero/teams/bot", () => { token: teamsToken(), }); expect(firstResponse.status).toBe(200); - const firstRunId = dispatchRunId((await firstResponse.json()).dispatch); + const firstBody = await readTeamsBotResponseAndFlush(firstResponse); + expect(firstBody).not.toHaveProperty("dispatch"); + const firstRunId = await runIdForPrompt(actor, "active run one"); const secondResponse = await postTeamsActivity({ activity: teamsPersonalMessageActivity({ @@ -939,7 +1211,9 @@ describe("POST /api/zero/teams/bot", () => { token: teamsToken(), }); expect(secondResponse.status).toBe(200); - const secondRunId = dispatchRunId((await secondResponse.json()).dispatch); + const secondBody = await readTeamsBotResponseAndFlush(secondResponse); + expect(secondBody).not.toHaveProperty("dispatch"); + const secondRunId = await runIdForPrompt(actor, "active run two"); const queuedResponse = await postTeamsActivity({ activity: teamsPersonalMessageActivity({ @@ -950,15 +1224,30 @@ describe("POST /api/zero/teams/bot", () => { token: teamsToken(), }); expect(queuedResponse.status).toBe(200); - const queuedBody = await queuedResponse.json(); - expect(queuedBody.dispatch).toMatchObject({ - kind: "queued", - runId: expect.any(String), - }); - const queuedRunId = dispatchRunId(queuedBody.dispatch); + const queuedBody = await readTeamsBotResponseAndFlush(queuedResponse); + expect(queuedBody).not.toHaveProperty("dispatch"); + const queuedRunId = await runIdForPrompt(actor, "queued run three"); - expect(outboundRequests).toHaveLength(1); - expect(outboundRequests[0]).toMatchObject({ + expect(outboundRequests).toHaveLength(4); + expect( + outboundRequests.slice(0, 3).map((request) => { + return request.body; + }), + ).toStrictEqual([ + { + type: "typing", + channelData: { tenant: { id: fixture.teamsTenantId } }, + }, + { + type: "typing", + channelData: { tenant: { id: fixture.teamsTenantId } }, + }, + { + type: "typing", + channelData: { tenant: { id: fixture.teamsTenantId } }, + }, + ]); + expect(outboundRequests[3]).toMatchObject({ activityId: "activity-queue-third", body: { type: "message", @@ -984,13 +1273,119 @@ describe("POST /api/zero/teams/bot", () => { ], }, }); - expect(outboundRequests[0]?.body).not.toHaveProperty("text"); + expect(outboundRequests[3]?.body).not.toHaveProperty("text"); + expect(outboundRequests.reactions).toHaveLength(0); await runsApi.requestCancelRun(actor, queuedRunId, [200]); await runsApi.requestCancelRun(actor, firstRunId, [200]); await runsApi.requestCancelRun(actor, secondRunId, [200]); }); + it("sends typing and adds audit/footer text for Teams run pre-dispatch failures", async () => { + const fixture = await trackTeamsFixture( + Promise.resolve(teamsConnectFixture()), + ); + const actor = authOrgApi.user({ + userId: fixture.userId, + orgId: fixture.orgId, + orgRole: "org:admin", + }); + context.mocks.ably.publish.mockResolvedValue(undefined); + authOrgApi.acceptAgentStorageWrites(); + const defaultAgent = await authOrgApi.createAgent(actor, { + displayName: "Teams default agent", + visibility: "public", + }); + await authOrgApi.setDefaultAgent(actor, defaultAgent.agentId); + const supportAgent = await authOrgApi.createAgent(actor, { + displayName: "Teams support agent", + visibility: "public", + }); + context.mocks.clerk.users.getOrganizationMembershipList.mockResolvedValue({ + data: [ + { + organization: { id: fixture.orgId }, + role: "org:admin", + }, + ], + }); + await updateFeatureSwitchesForUser( + context, + { + userId: fixture.userId, + orgId: fixture.orgId, + orgRole: "org:admin", + }, + { + [FeatureSwitchKey.ZeroDebug]: true, + }, + ); + botFrameworkHandlers(); + const outboundRequests = teamsOutboundHandlers( + fixture.serviceUrl, + fixture.teamsTenantId, + ); + + const installResponse = await postTeamsActivity({ + activity: teamsMessageActivity(fixture), + token: teamsToken(), + }); + expect(installResponse.status).toBe(200); + await installResponse.json(); + await flushWaitUntilForTest(); + await connectTeamsFixture(fixture); + + outboundRequests.splice(0, outboundRequests.length); + const switchResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-failure-switch-agent", + text: "", + value: { + zeroTeamsAction: "switch_agent", + selectedComposeId: supportAgent.agentId, + }, + }), + token: teamsToken(), + }); + expect(switchResponse.status).toBe(200); + const switchBody = await readTeamsBotResponseAndFlush(switchResponse); + expect(switchBody).not.toHaveProperty("dispatch"); + + outboundRequests.splice(0, outboundRequests.length); + const failedResponse = await postTeamsActivity({ + activity: teamsPersonalMessageActivity({ + fixture, + id: "activity-run-pre-dispatch-failure", + text: "run without entitlement", + }), + token: teamsToken(), + }); + expect(failedResponse.status).toBe(200); + const body = await readTeamsBotResponseAndFlush(failedResponse); + expect(body).not.toHaveProperty("dispatch"); + + expect(outboundRequests).toHaveLength(2); + expect(outboundRequests[0]).toMatchObject({ + body: { + type: "typing", + channelData: { tenant: { id: fixture.teamsTenantId } }, + }, + }); + expect(outboundRequests[1]).toMatchObject({ + activityId: "activity-run-pre-dispatch-failure", + body: { + type: "message", + text: expect.stringContaining(`[Audit](${APP_ORIGIN}/activities)`), + textFormat: "markdown", + }, + }); + expect(outboundRequests[1]?.body).toMatchObject({ + text: expect.stringContaining("Sent via Teams support agent"), + }); + expect(outboundRequests.reactions).toHaveLength(0); + }); + it("dispatches connected Teams messages to the org default agent", async () => { const fixture = await trackTeamsFixture( Promise.resolve(teamsConnectFixture()), @@ -1013,14 +1408,21 @@ describe("POST /api/zero/teams/bot", () => { await runsApi.grantProEntitlement(actor); await runsApi.ensureOrgModelProvider(actor); botFrameworkHandlers(); - teamsOutboundHandlers(fixture.serviceUrl, fixture.teamsTenantId); + const outboundRequests = teamsOutboundHandlers( + fixture.serviceUrl, + fixture.teamsTenantId, + ); const installResponse = await postTeamsActivity({ activity: teamsMessageActivity(fixture), token: teamsToken(), }); expect(installResponse.status).toBe(200); + await installResponse.json(); + await flushWaitUntilForTest(); await connectTeamsFixture(fixture); + outboundRequests.splice(0, outboundRequests.length); + outboundRequests.reactions.splice(0, outboundRequests.reactions.length); const channelMessages: TeamsGraphMessageFixture[] = []; const threadRoots: Record = { @@ -1084,8 +1486,23 @@ describe("POST /api/zero/teams/bot", () => { token: teamsToken(), }); expect(channelContextResponse.status).toBe(200); - const channelContextBody = await channelContextResponse.json(); - const channelContextRunId = dispatchRunId(channelContextBody.dispatch); + const channelContextBody = await readTeamsBotResponseAndFlush( + channelContextResponse, + ); + expect(channelContextBody).not.toHaveProperty("dispatch"); + expect(outboundRequests).toHaveLength(0); + expect(outboundRequests.reactions).toStrictEqual([ + { + method: "PUT", + conversationId: "19:thread@thread.tacv2", + activityId: "activity-channel-context-1", + reactionType: "1f4ad_thoughtballoon", + }, + ]); + const channelContextRunId = await runIdForPrompt( + actor, + "start another topic", + ); await runsApi.heartbeatRunner(runnerGroup); const channelContextClaim = await runsApi.claimRunnerJob(channelContextRunId); @@ -1130,13 +1547,25 @@ describe("POST /api/zero/teams/bot", () => { }); expect(response.status).toBe(200); - const body = await response.json(); - expect(body.dispatch).toMatchObject({ - kind: expect.stringMatching(/^(accepted|queued)$/u), - runId: expect.any(String), - }); + const body = await readTeamsBotResponseAndFlush(response); + expect(body).not.toHaveProperty("dispatch"); + expect(outboundRequests).toHaveLength(0); + expect(outboundRequests.reactions).toStrictEqual([ + { + method: "PUT", + conversationId: "19:thread@thread.tacv2", + activityId: "activity-channel-context-1", + reactionType: "1f4ad_thoughtballoon", + }, + { + method: "PUT", + conversationId: "19:thread@thread.tacv2", + activityId: "activity-dispatch-1", + reactionType: "1f4ad_thoughtballoon", + }, + ]); - const runId = dispatchRunId(body.dispatch); + const runId = await runIdForPrompt(actor, "ship the Teams dispatch"); await runsApi.heartbeatRunner(runnerGroup); const claim = await runsApi.claimRunnerJob(runId); const appendSystemPrompt = claim.appendSystemPrompt ?? ""; @@ -1205,6 +1634,72 @@ describe("POST /api/zero/teams/bot", () => { expect(graphRequests).toContain("thread-replies:root-dispatch"); }); + it("includes Teams thread computer use host bindings in queued zero tokens", async () => { + const { fixture, actor, runnerGroup } = await setupConnectedTeamsBotActor(); + const host = await computerUseApi.startComputerUseHost(actor, { + hostName: "Teams authorized host", + }); + const threadId = "teams-computer-use-thread"; + + const firstResponse = await postTeamsActivity({ + activity: teamsPersonalThreadMessageActivity({ + fixture, + id: "activity-computer-use-authorize", + threadId, + text: "authorize the browser", + }), + token: teamsToken(), + }); + expect(firstResponse.status).toBe(200); + const firstBody = await readTeamsBotResponseAndFlush(firstResponse); + expect(firstBody).not.toHaveProperty("dispatch"); + + const firstRunId = await runIdForPrompt(actor, "authorize the browser"); + await runsApi.heartbeatRunner(runnerGroup); + const firstClaim = await runsApi.claimRunnerJob(firstRunId); + const firstZeroToken = firstClaim.environment?.ZERO_TOKEN; + if (!firstZeroToken) { + throw new Error("Claimed Teams runner job did not include ZERO_TOKEN"); + } + const created = await computerUseApi.createComputerUseAuthorizationRequest({ + bearer: firstZeroToken, + }); + const requestToken = requestTokenFromUrl(created.authorizationUrl); + await computerUseApi.applyComputerUseAuthorizationRequest( + actor, + requestToken, + host.hostId, + ); + await runsApi.requestCancelRun(actor, firstRunId, [200]); + + const secondResponse = await postTeamsActivity({ + activity: teamsPersonalThreadMessageActivity({ + fixture, + id: "activity-computer-use-resume", + threadId, + text: "use the browser", + }), + token: teamsToken(), + }); + expect(secondResponse.status).toBe(200); + const secondBody = await readTeamsBotResponseAndFlush(secondResponse); + expect(secondBody).not.toHaveProperty("dispatch"); + + const secondRunId = await runIdForPrompt(actor, "use the browser"); + await runsApi.heartbeatRunner(runnerGroup); + const secondClaim = await runsApi.claimRunnerJob(secondRunId); + const secondZeroToken = secondClaim.environment?.ZERO_TOKEN; + if (!secondZeroToken) { + throw new Error("Claimed Teams runner job did not include ZERO_TOKEN"); + } + const zeroAuth = verifyZeroToken(secondZeroToken); + expect(zeroAuth).toMatchObject({ computerUseHostId: host.hostId }); + expect(zeroAuth?.capabilities).toContain("computer-use:write"); + + await runsApi.requestCancelRun(actor, secondRunId, [200]); + await computerUseApi.deleteComputerUseHost(actor, host.hostId); + }); + it("asks connected Teams users to configure a default agent", async () => { const fixture = await trackTeamsFixture( Promise.resolve(teamsConnectFixture()), @@ -1220,6 +1715,8 @@ describe("POST /api/zero/teams/bot", () => { token: teamsToken(), }); expect(installResponse.status).toBe(200); + await installResponse.json(); + await flushWaitUntilForTest(); await connectTeamsFixture(fixture); const response = await postTeamsActivity({ @@ -1231,12 +1728,8 @@ describe("POST /api/zero/teams/bot", () => { }); expect(response.status).toBe(200); - await expect(response.json()).resolves.toMatchObject({ - dispatch: { - kind: "notice", - replyText: expect.stringContaining("No agent is configured"), - }, - }); + const body = await readTeamsBotResponseAndFlush(response); + expect(body).not.toHaveProperty("dispatch"); expect(outboundRequests.at(-1)).toMatchObject({ activityId: "activity-no-default", body: { @@ -1285,6 +1778,7 @@ describe("POST /api/zero/teams/bot", () => { activity: teamsMessageActivity(), token: teamsToken(), }); + await flushWaitUntilForTest(); mocks.clerk.session(fixture.userId, fixture.orgId, "org:admin"); const client = setupApp({ context })(zeroTeamsConnectContract); await accept( diff --git a/turbo/apps/api/src/signals/routes/__tests__/zero-teams-connect.test.ts b/turbo/apps/api/src/signals/routes/__tests__/zero-teams-connect.test.ts index 4a750ce0a52..c743ccf48ca 100644 --- a/turbo/apps/api/src/signals/routes/__tests__/zero-teams-connect.test.ts +++ b/turbo/apps/api/src/signals/routes/__tests__/zero-teams-connect.test.ts @@ -444,14 +444,13 @@ describe("POST /api/zero/integrations/teams/connect", () => { content: { type: "AdaptiveCard", version: "1.4", - body: expect.arrayContaining([ - expect.objectContaining({ - text: expect.stringContaining("You're connected"), - }), - expect.objectContaining({ - text: expect.stringContaining("Hi! I'm Zero"), - }), - ]), + body: [ + { + type: "TextBlock", + text: "You're connected! 🎉\nMention `@Zero` in any channel or send a DM to start chatting with your agent.", + wrap: true, + }, + ], }, }, ], diff --git a/turbo/apps/api/src/signals/routes/test-computer-use-state.ts b/turbo/apps/api/src/signals/routes/test-computer-use-state.ts index a00de61c29f..9245e5e17b2 100644 --- a/turbo/apps/api/src/signals/routes/test-computer-use-state.ts +++ b/turbo/apps/api/src/signals/routes/test-computer-use-state.ts @@ -10,6 +10,9 @@ import { chatThreads } from "@vm0/db/schema/chat-thread"; import { slackOrgConnections } from "@vm0/db/schema/slack-org-connection"; import { slackOrgInstallations } from "@vm0/db/schema/slack-org-installation"; import { slackOrgThreadSessions } from "@vm0/db/schema/slack-org-thread-session"; +import { teamsOrgConnections } from "@vm0/db/schema/teams-org-connection"; +import { teamsOrgInstallations } from "@vm0/db/schema/teams-org-installation"; +import { teamsOrgThreadSessions } from "@vm0/db/schema/teams-org-thread-session"; import { zeroRuns } from "@vm0/db/schema/zero-run"; import { and, eq } from "drizzle-orm"; @@ -18,6 +21,7 @@ import { request$ } from "../context/hono"; import { writeDb$, type Db } from "../external/db"; import type { RouteEntry } from "../route-entry"; import { slackOrgCallbackPayloadSchema } from "../services/slack-org-callback-payload"; +import { teamsOrgCallbackPayloadSchema } from "../services/teams-org-callback-payload"; import { isTestEndpointAllowed, testEndpointNotFoundResponse, @@ -37,6 +41,27 @@ interface RunState { readonly chatThreadId: string | null; } +interface BaseComputerUseRunSeed { + readonly composeId: string; + readonly sessionId: string; + readonly runId: string; + readonly threadId: string | null; +} + +interface SlackComputerUseSeed { + readonly connection_id: string; + readonly channel_id: string; + readonly thread_ts: string; +} + +interface TeamsComputerUseSeed { + readonly connection_id: string; + readonly conversation_id: string; + readonly thread_id: string; +} + +type ComputerUseTriggerSource = "web" | "slack" | "teams"; + async function loadRunState(db: Db, runId: string): Promise { const [run] = await db .select({ @@ -71,6 +96,21 @@ async function slackCallbackPayload(db: Db, runId: string) { return parsed.success ? parsed.data : null; } +async function teamsCallbackPayload(db: Db, runId: string) { + const [callback] = await db + .select({ payload: agentRunCallbacks.payload }) + .from(agentRunCallbacks) + .where( + and( + eq(agentRunCallbacks.runId, runId), + eq(agentRunCallbacks.internalKind, "teams:org"), + ), + ) + .limit(1); + const parsed = teamsOrgCallbackPayloadSchema.safeParse(callback?.payload); + return parsed.success ? parsed.data : null; +} + async function sourceComputerUseHostId( db: Db, run: RunState, @@ -103,9 +143,208 @@ async function sourceComputerUseHostId( return session?.computerUseHostId ?? null; } + if (run.triggerSource === "teams") { + const payload = await teamsCallbackPayload(db, run.id); + if (!payload) { + return null; + } + const [session] = await db + .select({ computerUseHostId: teamsOrgThreadSessions.computerUseHostId }) + .from(teamsOrgThreadSessions) + .where( + and( + eq(teamsOrgThreadSessions.connectionId, payload.connectionId), + eq( + teamsOrgThreadSessions.teamsConversationId, + payload.conversationId, + ), + eq(teamsOrgThreadSessions.teamsThreadId, payload.threadId), + ), + ) + .limit(1); + return session?.computerUseHostId ?? null; + } + return null; } +async function seedBaseComputerUseRun(args: { + readonly db: Db; + readonly userId: string; + readonly orgId: string; + readonly triggerSource: ComputerUseTriggerSource; + readonly signal: AbortSignal; +}): Promise { + const composeId = randomUUID(); + const sessionId = randomUUID(); + const runId = randomUUID(); + const threadId = args.triggerSource === "web" ? randomUUID() : null; + + await args.db.insert(agentComposes).values({ + id: composeId, + userId: args.userId, + orgId: args.orgId, + name: `computer-use-auth-${composeId.slice(0, 8)}`, + }); + args.signal.throwIfAborted(); + + if (threadId) { + await args.db.insert(chatThreads).values({ + id: threadId, + userId: args.userId, + agentComposeId: composeId, + title: "Computer Use authorization test", + }); + args.signal.throwIfAborted(); + } + + await args.db.insert(agentSessions).values({ + id: sessionId, + userId: args.userId, + orgId: args.orgId, + agentComposeId: composeId, + }); + args.signal.throwIfAborted(); + + await args.db.insert(agentRuns).values({ + id: runId, + userId: args.userId, + orgId: args.orgId, + sessionId, + status: "running", + prompt: "Need Computer Use", + }); + args.signal.throwIfAborted(); + + await args.db.insert(zeroRuns).values({ + id: runId, + triggerSource: args.triggerSource, + chatThreadId: threadId, + }); + args.signal.throwIfAborted(); + + return { composeId, sessionId, runId, threadId }; +} + +async function seedSlackComputerUseCallback(args: { + readonly db: Db; + readonly userId: string; + readonly orgId: string; + readonly runId: string; + readonly composeId: string; + readonly signal: AbortSignal; +}): Promise { + const workspaceId = `T${randomUUID().replaceAll("-", "").slice(0, 10)}`; + const slackUserId = `U${randomUUID().replaceAll("-", "").slice(0, 10)}`; + const channelId = `C${randomUUID().replaceAll("-", "").slice(0, 10)}`; + const threadTs = "1710000000.000100"; + const connectionId = randomUUID(); + + await args.db.insert(slackOrgInstallations).values({ + slackWorkspaceId: workspaceId, + slackWorkspaceName: "Computer Use Auth Workspace", + orgId: args.orgId, + encryptedBotToken: "encrypted-bot-token", + botUserId: "U_BOT_TEST", + }); + args.signal.throwIfAborted(); + + await args.db.insert(slackOrgConnections).values({ + id: connectionId, + slackWorkspaceId: workspaceId, + slackUserId, + vm0UserId: args.userId, + }); + args.signal.throwIfAborted(); + + await args.db.insert(agentRunCallbacks).values({ + runId: args.runId, + internalKind: "slack:org", + encryptedSecret: "encrypted-callback-secret", + payload: { + workspaceId, + channelId, + threadTs, + messageTs: threadTs, + connectionId, + agentId: args.composeId, + }, + }); + args.signal.throwIfAborted(); + + return { + connection_id: connectionId, + channel_id: channelId, + thread_ts: threadTs, + }; +} + +async function seedTeamsComputerUseCallback(args: { + readonly db: Db; + readonly userId: string; + readonly orgId: string; + readonly runId: string; + readonly composeId: string; + readonly signal: AbortSignal; +}): Promise { + const tenantId = `tenant-${randomUUID()}`; + const conversationId = `19:${randomUUID()}@thread.tacv2`; + const threadId = `root-${randomUUID()}`; + const connectionId = randomUUID(); + const teamsUserId = `29:${randomUUID()}`; + + await args.db.insert(teamsOrgInstallations).values({ + teamsTenantId: tenantId, + teamsTenantName: "Computer Use Auth Tenant", + orgId: args.orgId, + botId: "28:bot-test", + botName: "Zero", + serviceUrl: "https://smba.trafficmanager.net/amer/", + }); + args.signal.throwIfAborted(); + + await args.db.insert(teamsOrgConnections).values({ + id: connectionId, + teamsTenantId: tenantId, + teamsUserId, + vm0UserId: args.userId, + }); + args.signal.throwIfAborted(); + + await args.db.insert(agentRunCallbacks).values({ + runId: args.runId, + internalKind: "teams:org", + encryptedSecret: "encrypted-callback-secret", + payload: { + tenantId, + tenantName: "Computer Use Auth Tenant", + teamId: null, + teamName: null, + channelId: null, + conversationId, + conversationType: "personal", + threadId, + activityId: threadId, + serviceUrl: "https://smba.trafficmanager.net/amer/", + connectionId, + teamsUserId, + teamsUserDisplayName: "Computer Use Tester", + teamsUserPrincipalName: "tester@example.com", + botId: "28:bot-test", + botName: "Zero", + agentId: args.composeId, + existingSessionId: null, + }, + }); + args.signal.throwIfAborted(); + + return { + connection_id: connectionId, + conversation_id: conversationId, + thread_id: threadId, + }; +} + const postComputerUseState$ = command( async ({ get, set }, signal: AbortSignal) => { if (!isTestEndpointAllowed(get(request$))) { @@ -127,113 +366,46 @@ const postComputerUseState$ = command( } const db = set(writeDb$); - const composeId = randomUUID(); - const sessionId = randomUUID(); - const runId = randomUUID(); - const threadId = body.trigger_source === "web" ? randomUUID() : null; - - await db.insert(agentComposes).values({ - id: composeId, + const seed = await seedBaseComputerUseRun({ + db, userId: body.user_id, orgId: body.org_id, - name: `computer-use-auth-${composeId.slice(0, 8)}`, - }); - signal.throwIfAborted(); - - if (threadId) { - await db.insert(chatThreads).values({ - id: threadId, - userId: body.user_id, - agentComposeId: composeId, - title: "Computer Use authorization test", - }); - signal.throwIfAborted(); - } - - await db.insert(agentSessions).values({ - id: sessionId, - userId: body.user_id, - orgId: body.org_id, - agentComposeId: composeId, - }); - signal.throwIfAborted(); - - await db.insert(agentRuns).values({ - id: runId, - userId: body.user_id, - orgId: body.org_id, - sessionId, - status: "running", - prompt: "Need Computer Use", - }); - signal.throwIfAborted(); - - await db.insert(zeroRuns).values({ - id: runId, triggerSource: body.trigger_source, - chatThreadId: threadId, + signal, }); - signal.throwIfAborted(); - - let slack: { - readonly connection_id: string; - readonly channel_id: string; - readonly thread_ts: string; - } | null = null; - - if (body.trigger_source === "slack") { - const workspaceId = `T${randomUUID().replaceAll("-", "").slice(0, 10)}`; - const slackUserId = `U${randomUUID().replaceAll("-", "").slice(0, 10)}`; - const channelId = `C${randomUUID().replaceAll("-", "").slice(0, 10)}`; - const threadTs = "1710000000.000100"; - const connectionId = randomUUID(); - - await db.insert(slackOrgInstallations).values({ - slackWorkspaceId: workspaceId, - slackWorkspaceName: "Computer Use Auth Workspace", - orgId: body.org_id, - encryptedBotToken: "encrypted-bot-token", - botUserId: "U_BOT_TEST", - }); - signal.throwIfAborted(); - await db.insert(slackOrgConnections).values({ - id: connectionId, - slackWorkspaceId: workspaceId, - slackUserId, - vm0UserId: body.user_id, - }); - signal.throwIfAborted(); - await db.insert(agentRunCallbacks).values({ - runId, - internalKind: "slack:org", - encryptedSecret: "encrypted-callback-secret", - payload: { - workspaceId, - channelId, - threadTs, - messageTs: threadTs, - connectionId, - agentId: composeId, - }, - }); - signal.throwIfAborted(); - - slack = { - connection_id: connectionId, - channel_id: channelId, - thread_ts: threadTs, - }; - } + const slack = + body.trigger_source === "slack" + ? await seedSlackComputerUseCallback({ + db, + userId: body.user_id, + orgId: body.org_id, + runId: seed.runId, + composeId: seed.composeId, + signal, + }) + : null; + const teams = + body.trigger_source === "teams" + ? await seedTeamsComputerUseCallback({ + db, + userId: body.user_id, + orgId: body.org_id, + runId: seed.runId, + composeId: seed.composeId, + signal, + }) + : null; return { status: 200 as const, body: { ok: true as const, - compose_id: composeId, - run_id: runId, - session_id: sessionId, - thread_id: threadId, + compose_id: seed.composeId, + run_id: seed.runId, + session_id: seed.sessionId, + thread_id: seed.threadId, slack, + teams, }, }; }, @@ -268,7 +440,9 @@ const getComputerUseState$ = command( status: 200 as const, body: { source: - run.triggerSource === "web" || run.triggerSource === "slack" + run.triggerSource === "web" || + run.triggerSource === "slack" || + run.triggerSource === "teams" ? run.triggerSource : null, computer_use_host_id: await sourceComputerUseHostId(db, run), @@ -320,6 +494,25 @@ const deleteComputerUseState$ = command( signal.throwIfAborted(); } + const teamsPayload = await teamsCallbackPayload(db, run.id); + signal.throwIfAborted(); + if (teamsPayload) { + await db + .delete(teamsOrgThreadSessions) + .where( + eq(teamsOrgThreadSessions.connectionId, teamsPayload.connectionId), + ); + signal.throwIfAborted(); + await db + .delete(teamsOrgConnections) + .where(eq(teamsOrgConnections.id, teamsPayload.connectionId)); + signal.throwIfAborted(); + await db + .delete(teamsOrgInstallations) + .where(eq(teamsOrgInstallations.teamsTenantId, teamsPayload.tenantId)); + signal.throwIfAborted(); + } + await db .delete(agentRunCallbacks) .where(eq(agentRunCallbacks.runId, run.id)); diff --git a/turbo/apps/api/src/signals/routes/zero-teams-bot.ts b/turbo/apps/api/src/signals/routes/zero-teams-bot.ts index d764b0ade95..102db1b8e0c 100644 --- a/turbo/apps/api/src/signals/routes/zero-teams-bot.ts +++ b/turbo/apps/api/src/signals/routes/zero-teams-bot.ts @@ -1,5 +1,9 @@ import { command } from "ccstate"; -import { zeroTeamsBotContract } from "@vm0/api-contracts/contracts/zero-teams-bot"; +import { + zeroTeamsBotContract, + type TeamsInboundActivity, +} from "@vm0/api-contracts/contracts/zero-teams-bot"; +import { teamsOrgInstallations } from "@vm0/db/schema/teams-org-installation"; import { normalizeTeamsActivity, @@ -10,7 +14,9 @@ import { env } from "../../lib/env"; import { verifyTeamsBotAuthorization } from "../../lib/teams-bot-auth"; import { logger } from "../../lib/log"; import { authorization$, request$ } from "../context/hono"; +import { waitUntil } from "../context/wait-until"; import { + deleteTeamsReaction, sendTeamsMessageReply, type TeamsAdaptiveCard, } from "../external/teams-bot-client"; @@ -22,11 +28,13 @@ import { recordTeamsInstallationActivity$, } from "../services/zero-teams-connect.service"; import { dispatchTeamsMessageToAgent$ } from "../services/zero-teams-dispatch.service"; -import { safeJsonParse } from "../utils"; +import { ApiDispatchTimingCollector } from "../services/api-dispatch-timing.service"; +import { safeJsonParse, settle, tapError } from "../utils"; const L = logger("TeamsBot"); const TEAMS_LOGIN_PROMPT_CARD_TEXT = "Please connect your account to use Zero in this Teams workspace."; +const TEAMS_THINKING_REACTION_TYPE = "1f4ad_thoughtballoon"; function errorResponse( status: 400 | 401 | 403 | 503, @@ -110,9 +118,151 @@ function buildTeamsQueueCard(args: { }; } +type TeamsDispatchReplySource = + | { + readonly kind: "notice"; + readonly replyText: string; + readonly connectUrl?: string; + readonly card?: TeamsAdaptiveCard; + } + | { readonly kind: "failed"; readonly replyText: string } + | { readonly kind: "queued" } + | { readonly kind: "ignored" | "accepted" }; + +type TeamsMessageActivity = Extract; +type TeamsInstallation = typeof teamsOrgInstallations.$inferSelect; + +function dispatchReplyContent(dispatch: TeamsDispatchReplySource): { + readonly replyText: string | null; + readonly card?: TeamsAdaptiveCard; +} { + if (dispatch.kind === "notice") { + return { + replyText: dispatch.replyText, + ...(dispatch.card + ? { card: dispatch.card } + : dispatch.connectUrl + ? { + card: buildTeamsLoginPromptCard({ + connectUrl: dispatch.connectUrl, + }), + } + : {}), + }; + } + if (dispatch.kind === "failed") { + return { replyText: dispatch.replyText }; + } + if (dispatch.kind === "queued") { + const url = queueUrl(); + return { + replyText: buildTeamsQueueText(url), + card: buildTeamsQueueCard({ url }), + }; + } + return { replyText: null }; +} + +async function clearTeamsThinkingReactionForActivity(args: { + readonly activity: TeamsMessageActivity; + readonly signal: AbortSignal; +}): Promise { + if ( + args.activity.conversationType === "personal" || + !args.activity.activityId + ) { + return; + } + + const result = await settle( + deleteTeamsReaction({ + serviceUrl: args.activity.serviceUrl, + conversationId: args.activity.conversationId, + activityId: args.activity.activityId, + tenantId: args.activity.tenantId, + reactionType: TEAMS_THINKING_REACTION_TYPE, + signal: args.signal, + }), + args.signal, + ); + const error = !result.ok + ? result.error + : result.value.kind === "teams-error" + ? result.value.error + : undefined; + if (error !== undefined) { + L.debug("Failed to clear Teams thinking reaction after dispatch failure", { + tenantId: args.activity.tenantId, + conversationId: args.activity.conversationId, + activityId: args.activity.activityId, + error, + }); + } +} + +const dispatchTeamsMessageAndReply$ = command( + async ( + { set }, + args: { + readonly activity: TeamsMessageActivity; + readonly installation: TeamsInstallation | null; + readonly apiStartTime: number; + readonly timing: ApiDispatchTimingCollector; + }, + signal: AbortSignal, + ): Promise => { + const dispatch = await set( + dispatchTeamsMessageToAgent$, + { + activity: args.activity, + installation: args.installation, + apiStartTime: args.apiStartTime, + timing: args.timing, + }, + signal, + ); + signal.throwIfAborted(); + + if (dispatch.kind === "failed") { + await clearTeamsThinkingReactionForActivity({ + activity: args.activity, + signal, + }); + signal.throwIfAborted(); + } + + const { replyText, card } = dispatchReplyContent(dispatch); + if (!replyText) { + return; + } + + const reply = await sendTeamsMessageReply({ + serviceUrl: args.activity.serviceUrl, + conversationId: args.activity.conversationId, + activityId: args.activity.activityId ?? undefined, + tenantId: args.activity.tenantId, + text: replyText, + ...(card ? { card } : {}), + signal, + }); + signal.throwIfAborted(); + + if (reply.kind === "teams-error") { + L.warn("Teams dispatch reply failed", { + tenantId: args.activity.tenantId, + conversationId: args.activity.conversationId, + activityId: args.activity.activityId, + status: reply.status, + error: reply.error, + }); + } + }, +); + const handleZeroTeamsBot$ = command( async ({ get, set }, signal: AbortSignal) => { const request = get(request$); + const apiStartTime = now(); const bodyText = await request.text(); signal.throwIfAborted(); @@ -167,68 +317,30 @@ const handleZeroTeamsBot$ = command( const installation = activityResult.kind === "upserted" ? activityResult.installation : null; - const dispatch = await set( - dispatchTeamsMessageToAgent$, - { - activity: normalized.activity, - installation, - apiStartTime: now(), - }, - signal, + const timing = new ApiDispatchTimingCollector(); + timing.recordElapsed( + "api_dispatch_pre_create_zero_teams_entrypoint_gap", + "nested", + apiStartTime, ); - signal.throwIfAborted(); - if (normalized.activity.kind === "message") { - const queueNoticeUrl = dispatch.kind === "queued" ? queueUrl() : null; - const replyText = - dispatch.kind === "notice" || dispatch.kind === "failed" - ? dispatch.replyText - : queueNoticeUrl - ? buildTeamsQueueText(queueNoticeUrl) - : null; - const card = - dispatch.kind === "notice" && dispatch.connectUrl - ? buildTeamsLoginPromptCard({ - connectUrl: dispatch.connectUrl, - }) - : queueNoticeUrl - ? buildTeamsQueueCard({ url: queueNoticeUrl }) - : undefined; - if (!replyText) { - return { - status: 200 as const, - body: { - ok: true as const, - activity: normalized.activity, - connectUrl: buildTeamsConnectUrlForActivity({ + waitUntil( + tapError( + set( + dispatchTeamsMessageAndReply$, + { activity: normalized.activity, installation, - }), - dispatch, + apiStartTime, + timing, + }, + signal, + ), + (error) => { + L.error("Error handling Teams message activity", { error }); }, - }; - } - - const reply = await sendTeamsMessageReply({ - serviceUrl: normalized.activity.serviceUrl, - conversationId: normalized.activity.conversationId, - activityId: normalized.activity.activityId ?? undefined, - tenantId: normalized.activity.tenantId, - text: replyText, - ...(card ? { card } : {}), - signal, - }); - signal.throwIfAborted(); - - if (reply.kind === "teams-error") { - L.warn("Teams dispatch reply failed", { - tenantId: normalized.activity.tenantId, - conversationId: normalized.activity.conversationId, - activityId: normalized.activity.activityId, - status: reply.status, - error: reply.error, - }); - } + ), + ); } return { @@ -240,7 +352,6 @@ const handleZeroTeamsBot$ = command( activity: normalized.activity, installation, }), - dispatch, }, }; }, diff --git a/turbo/apps/api/src/signals/services/agent-run-callbacks.service.ts b/turbo/apps/api/src/signals/services/agent-run-callbacks.service.ts index 8a0b30d616f..3d860fea929 100644 --- a/turbo/apps/api/src/signals/services/agent-run-callbacks.service.ts +++ b/turbo/apps/api/src/signals/services/agent-run-callbacks.service.ts @@ -13,6 +13,7 @@ import { userFeatureSwitchOverrides } from "./feature-switches.service"; import { handleAgentPhoneInternalCallback$ } from "./internal-agentphone-run-callback.service"; import { internalRunCallbackKindForRecord } from "./internal-run-callback"; import { handleSlackOrgInternalCallback$ } from "./internal-slack-org-run-callback.service"; +import { handleTeamsOrgInternalCallback$ } from "./internal-teams-org-run-callback.service"; import { handleTelegramInternalCallback$ } from "./internal-telegram-run-callback.service"; function resolveCallbackUrl(url: string): string { @@ -68,46 +69,33 @@ export const dispatchProgressCallbacks$ = command( await Promise.allSettled( callbacks.map(async (callback) => { const internalKind = internalRunCallbackKindForRecord(callback); + const progressCallback = { + callbackId: callback.id, + runId, + status: "progress" as const, + payload: callback.payload, + }; if (internalKind === "chat") { return; } if (internalKind === "agentphone") { await set( handleAgentPhoneInternalCallback$, - { - callbackId: callback.id, - runId, - status: "progress", - payload: callback.payload, - }, + progressCallback, signal, ); return; } if (internalKind === "slack:org") { - await set( - handleSlackOrgInternalCallback$, - { - callbackId: callback.id, - runId, - status: "progress", - payload: callback.payload, - }, - signal, - ); + await set(handleSlackOrgInternalCallback$, progressCallback, signal); + return; + } + if (internalKind === "teams:org") { + await set(handleTeamsOrgInternalCallback$, progressCallback, signal); return; } if (internalKind === "telegram") { - await set( - handleTelegramInternalCallback$, - { - callbackId: callback.id, - runId, - status: "progress", - payload: callback.payload, - }, - signal, - ); + await set(handleTelegramInternalCallback$, progressCallback, signal); return; } if (internalKind === "agent" || internalKind === "github:issues") { diff --git a/turbo/apps/api/src/signals/services/api-dispatch-timing.service.ts b/turbo/apps/api/src/signals/services/api-dispatch-timing.service.ts index 6ec5db68e70..163aec60dd2 100644 --- a/turbo/apps/api/src/signals/services/api-dispatch-timing.service.ts +++ b/turbo/apps/api/src/signals/services/api-dispatch-timing.service.ts @@ -64,6 +64,8 @@ export type ApiDispatchTimingActionType = | "api_dispatch_pre_create_zero_slack_build_run_params_user_info_resolver" | "api_dispatch_pre_create_zero_slack_build_run_params_assemble" | "api_dispatch_pre_create_zero_slack_create_run" + | "api_dispatch_pre_create_zero_teams_entrypoint_gap" + | "api_dispatch_pre_create_zero_teams_create_run" | "api_dispatch_pre_create_zero_workflow_trigger_entrypoint_gap" | "api_dispatch_pre_create_zero_workflow_trigger_check_active_run" | "api_dispatch_pre_create_zero_workflow_trigger_check_target_access" diff --git a/turbo/apps/api/src/signals/services/internal-teams-org-run-callback.service.ts b/turbo/apps/api/src/signals/services/internal-teams-org-run-callback.service.ts index 3b4f6c8fcee..1da3b6fb287 100644 --- a/turbo/apps/api/src/signals/services/internal-teams-org-run-callback.service.ts +++ b/turbo/apps/api/src/signals/services/internal-teams-org-run-callback.service.ts @@ -21,6 +21,7 @@ import { env } from "../../lib/env"; import { logger } from "../../lib/log"; import { writeDb$, type Db } from "../external/db"; import { + deleteTeamsReaction, sendTeamsMessageReply, sendTeamsTypingActivity, } from "../external/teams-bot-client"; @@ -44,6 +45,7 @@ import { const L = logger("InternalCallbacksTeamsOrg"); const ORG_SENTINEL_USER_ID = "__org__"; +const TEAMS_THINKING_REACTION_TYPE = "1f4ad_thoughtballoon"; type TerminalStatus = "completed" | "failed"; @@ -226,18 +228,54 @@ async function resolveRespondedByLabel(args: { return label ? `Responded by ${label}` : undefined; } +async function countThreadMentioners(args: { + readonly db: Db; + readonly tenantId: string; + readonly conversationId: string; + readonly threadId: string; + readonly currentConnectionId: string; +}): Promise { + const rows = await args.db + .select({ connectionId: teamsOrgThreadSessions.connectionId }) + .from(teamsOrgThreadSessions) + .innerJoin( + teamsOrgConnections, + eq(teamsOrgThreadSessions.connectionId, teamsOrgConnections.id), + ) + .where( + and( + eq(teamsOrgConnections.teamsTenantId, args.tenantId), + eq(teamsOrgThreadSessions.teamsConversationId, args.conversationId), + eq(teamsOrgThreadSessions.teamsThreadId, args.threadId), + ), + ); + return new Set([ + args.currentConnectionId, + ...rows.map((row) => { + return row.connectionId; + }), + ]).size; +} + async function resolveFooterText(args: { readonly db: Db; readonly orgId: string; readonly runId: string; readonly payload: TeamsOrgCallbackPayload; }): Promise { - const [respondedBy, modelLabel] = await Promise.all([ + const [respondedBy, mentionerCount, modelLabel] = await Promise.all([ resolveRespondedByLabel({ db: args.db, orgId: args.orgId, composeId: args.payload.agentId, }), + countThreadMentioners({ + db: args.db, + tenantId: args.payload.tenantId, + conversationId: args.payload.conversationId, + threadId: args.payload.threadId, + currentConnectionId: args.payload.connectionId, + }), resolveModelLabel({ db: args.db, orgId: args.orgId, @@ -249,7 +287,7 @@ async function resolveFooterText(args: { if (respondedBy) { parts.push(respondedBy); } - if (args.payload.conversationType !== "personal") { + if (args.payload.conversationType !== "personal" && mentionerCount > 1) { const replyTo = args.payload.teamsUserDisplayName ?? args.payload.teamsUserPrincipalName ?? @@ -302,7 +340,7 @@ function buildTeamsResponseText(args: { }): string { return [ args.mainText, - args.logsUrl ? `[View run details](${args.logsUrl})` : undefined, + args.logsUrl ? `[Audit](${args.logsUrl})` : undefined, args.footerText ? `_${args.footerText}_` : undefined, ] .filter((part): part is string => { @@ -425,7 +463,14 @@ async function handleProgress(args: { readonly payload: TeamsOrgCallbackPayload; readonly signal: AbortSignal; }): Promise { - const typingResult = await settle( + if ( + args.payload.conversationType !== "personal" && + args.payload.activityId !== null + ) { + return successResponse(); + } + + const indicatorResult = await settle( sendTeamsTypingActivity({ serviceUrl: args.payload.serviceUrl, conversationId: args.payload.conversationId, @@ -434,21 +479,60 @@ async function handleProgress(args: { }), args.signal, ); - const error = !typingResult.ok - ? typingResult.error - : typingResult.value.kind === "teams-error" - ? typingResult.value.error + const error = !indicatorResult.ok + ? indicatorResult.error + : indicatorResult.value.kind === "teams-error" + ? indicatorResult.value.error : undefined; if (error !== undefined) { L.debug("Failed to refresh Teams typing indicator", { tenantId: args.payload.tenantId, conversationId: args.payload.conversationId, + activityId: args.payload.activityId, error, }); } return successResponse(); } +async function clearTeamsThinkingReaction(args: { + readonly payload: TeamsOrgCallbackPayload; + readonly serviceUrl: string; + readonly signal: AbortSignal; +}): Promise { + if ( + args.payload.conversationType === "personal" || + args.payload.activityId === null + ) { + return; + } + + const result = await settle( + deleteTeamsReaction({ + serviceUrl: args.serviceUrl, + conversationId: args.payload.conversationId, + activityId: args.payload.activityId, + tenantId: args.payload.tenantId, + reactionType: TEAMS_THINKING_REACTION_TYPE, + signal: args.signal, + }), + args.signal, + ); + const error = !result.ok + ? result.error + : result.value.kind === "teams-error" + ? result.value.error + : undefined; + if (error !== undefined) { + L.debug("Failed to clear Teams thinking reaction", { + tenantId: args.payload.tenantId, + conversationId: args.payload.conversationId, + activityId: args.payload.activityId, + error, + }); + } +} + async function handleCompletion(args: { readonly db: Db; readonly runId: string; @@ -559,6 +643,13 @@ async function handleCompletion(args: { return teamsApiCallbackError(sendResult); } + await clearTeamsThinkingReaction({ + payload: args.payload, + serviceUrl, + signal: args.signal, + }); + args.signal.throwIfAborted(); + await saveOrgThreadSession({ db: args.db, payload: args.payload, diff --git a/turbo/apps/api/src/signals/services/zero-computer-use-authorization.service.ts b/turbo/apps/api/src/signals/services/zero-computer-use-authorization.service.ts index 2ff716db3ab..3f1d801508a 100644 --- a/turbo/apps/api/src/signals/services/zero-computer-use-authorization.service.ts +++ b/turbo/apps/api/src/signals/services/zero-computer-use-authorization.service.ts @@ -16,12 +16,16 @@ import { import { slackOrgConnections } from "@vm0/db/schema/slack-org-connection"; import { slackOrgInstallations } from "@vm0/db/schema/slack-org-installation"; import { slackOrgThreadSessions } from "@vm0/db/schema/slack-org-thread-session"; +import { teamsOrgConnections } from "@vm0/db/schema/teams-org-connection"; +import { teamsOrgInstallations } from "@vm0/db/schema/teams-org-installation"; +import { teamsOrgThreadSessions } from "@vm0/db/schema/teams-org-thread-session"; import { zeroRuns } from "@vm0/db/schema/zero-run"; import { env } from "../../lib/env"; import { nowDate } from "../../lib/time"; import { writeDb$, type Db } from "../external/db"; import { slackOrgCallbackPayloadSchema } from "./slack-org-callback-payload"; +import { teamsOrgCallbackPayloadSchema } from "./teams-org-callback-payload"; import { computerUseHostIsOnline, listComputerUseHosts$, @@ -44,6 +48,12 @@ type AuthorizationRequestScope = readonly slackConnectionId: string; readonly slackChannelId: string; readonly slackThreadTs: string; + } + | { + readonly source: "teams"; + readonly teamsConnectionId: string; + readonly teamsConversationId: string; + readonly teamsThreadId: string; }; type CreateComputerUseAuthorizationRequestResult = @@ -127,6 +137,34 @@ async function loadSlackScope(args: { }; } +async function loadTeamsScope(args: { + readonly db: Db; + readonly runId: string; +}): Promise { + const [callback] = await args.db + .select({ payload: agentRunCallbacks.payload }) + .from(agentRunCallbacks) + .where( + and( + eq(agentRunCallbacks.runId, args.runId), + eq(agentRunCallbacks.internalKind, "teams:org"), + ), + ) + .limit(1); + + const payload = teamsOrgCallbackPayloadSchema.safeParse(callback?.payload); + if (!payload.success) { + return null; + } + + return { + source: "teams", + teamsConnectionId: payload.data.connectionId, + teamsConversationId: payload.data.conversationId, + teamsThreadId: payload.data.threadId, + }; +} + async function resolveRequestScope(args: { readonly db: Db; readonly orgId: string; @@ -170,6 +208,13 @@ async function resolveRequestScope(args: { ); } + if (run.triggerSource === "teams") { + return ( + (await loadTeamsScope({ db: args.db, runId: args.runId })) ?? + "unsupported_context" + ); + } + return "unsupported_context"; } @@ -283,6 +328,32 @@ async function loadAuthorizedComputerUseHostId(args: { return threadSession?.computerUseHostId ?? null; } + if ( + args.request.source === "teams" && + args.request.teamsConnectionId && + args.request.teamsConversationId && + args.request.teamsThreadId + ) { + const [threadSession] = await args.db + .select({ computerUseHostId: teamsOrgThreadSessions.computerUseHostId }) + .from(teamsOrgThreadSessions) + .where( + and( + eq( + teamsOrgThreadSessions.connectionId, + args.request.teamsConnectionId, + ), + eq( + teamsOrgThreadSessions.teamsConversationId, + args.request.teamsConversationId, + ), + eq(teamsOrgThreadSessions.teamsThreadId, args.request.teamsThreadId), + ), + ) + .limit(1); + return threadSession?.computerUseHostId ?? null; + } + return null; } @@ -313,6 +384,151 @@ async function slackScopeExists(args: { return connection !== undefined; } +async function teamsScopeExists(args: { + readonly db: Db; + readonly orgId: string; + readonly userId: string; + readonly connectionId: string; +}): Promise { + const [connection] = await args.db + .select({ id: teamsOrgConnections.id }) + .from(teamsOrgConnections) + .innerJoin( + teamsOrgInstallations, + eq( + teamsOrgInstallations.teamsTenantId, + teamsOrgConnections.teamsTenantId, + ), + ) + .where( + and( + eq(teamsOrgConnections.id, args.connectionId), + eq(teamsOrgConnections.vm0UserId, args.userId), + eq(teamsOrgInstallations.orgId, args.orgId), + ), + ) + .limit(1); + return connection !== undefined; +} + +async function applyChatAuthorizationScope(args: { + readonly db: Db; + readonly request: AuthorizationRequestRow; + readonly userId: string; + readonly computerUseHostId: string; + readonly now: Date; +}): Promise { + const updated = await args.db + .update(chatThreads) + .set({ + computerUseHostId: args.computerUseHostId, + updatedAt: args.now, + }) + .where( + and( + eq(chatThreads.id, args.request.chatThreadId ?? ""), + eq(chatThreads.userId, args.userId), + ), + ) + .returning({ id: chatThreads.id }); + + return updated.length > 0; +} + +async function applySlackAuthorizationScope(args: { + readonly db: Db; + readonly request: AuthorizationRequestRow; + readonly orgId: string; + readonly userId: string; + readonly computerUseHostId: string; + readonly now: Date; +}): Promise { + const connectionId = args.request.slackConnectionId; + if ( + !connectionId || + !args.request.slackChannelId || + !args.request.slackThreadTs || + !(await slackScopeExists({ + db: args.db, + orgId: args.orgId, + userId: args.userId, + connectionId, + })) + ) { + return false; + } + + await args.db + .insert(slackOrgThreadSessions) + .values({ + connectionId, + slackChannelId: args.request.slackChannelId, + slackThreadTs: args.request.slackThreadTs, + computerUseHostId: args.computerUseHostId, + updatedAt: args.now, + }) + .onConflictDoUpdate({ + target: [ + slackOrgThreadSessions.connectionId, + slackOrgThreadSessions.slackChannelId, + slackOrgThreadSessions.slackThreadTs, + ], + set: { + computerUseHostId: args.computerUseHostId, + updatedAt: args.now, + }, + }); + + return true; +} + +async function applyTeamsAuthorizationScope(args: { + readonly db: Db; + readonly request: AuthorizationRequestRow; + readonly orgId: string; + readonly userId: string; + readonly computerUseHostId: string; + readonly now: Date; +}): Promise { + const connectionId = args.request.teamsConnectionId; + if ( + !connectionId || + !args.request.teamsConversationId || + !args.request.teamsThreadId || + !(await teamsScopeExists({ + db: args.db, + orgId: args.orgId, + userId: args.userId, + connectionId, + })) + ) { + return false; + } + + await args.db + .insert(teamsOrgThreadSessions) + .values({ + connectionId, + teamsConversationId: args.request.teamsConversationId, + teamsThreadId: args.request.teamsThreadId, + computerUseHostId: args.computerUseHostId, + updatedAt: args.now, + }) + .onConflictDoUpdate({ + target: [ + teamsOrgThreadSessions.connectionId, + teamsOrgThreadSessions.teamsConversationId, + teamsOrgThreadSessions.teamsThreadId, + ], + set: { + computerUseHostId: args.computerUseHostId, + updatedAt: args.now, + }, + }); + + return true; +} + export const createComputerUseAuthorizationRequest$ = command( async ( { set }, @@ -351,6 +567,11 @@ export const createComputerUseAuthorizationRequest$ = command( scope.source === "slack" ? scope.slackConnectionId : null, slackChannelId: scope.source === "slack" ? scope.slackChannelId : null, slackThreadTs: scope.source === "slack" ? scope.slackThreadTs : null, + teamsConnectionId: + scope.source === "teams" ? scope.teamsConnectionId : null, + teamsConversationId: + scope.source === "teams" ? scope.teamsConversationId : null, + teamsThreadId: scope.source === "teams" ? scope.teamsThreadId : null, expiresAt, createdAt: now, updatedAt: now, @@ -457,64 +678,37 @@ export const applyComputerUseAuthorizationRequest$ = command( signal.throwIfAborted(); const request = loaded.request; - if (request.source === "chat") { - const updated = await db - .update(chatThreads) - .set({ - computerUseHostId: args.computerUseHostId, - updatedAt: now, - }) - .where( - and( - eq(chatThreads.id, request.chatThreadId ?? ""), - eq(chatThreads.userId, args.userId), - ), - ) - .returning({ id: chatThreads.id }); - signal.throwIfAborted(); - - if (updated.length === 0) { - return { status: "scope_not_found" }; - } - } else if (request.source === "slack") { - const connectionId = request.slackConnectionId; - if ( - !connectionId || - !request.slackChannelId || - !request.slackThreadTs || - !(await slackScopeExists({ - db, - orgId: args.orgId, - userId: args.userId, - connectionId, - })) - ) { - return { status: "scope_not_found" }; - } - signal.throwIfAborted(); - - await db - .insert(slackOrgThreadSessions) - .values({ - connectionId, - slackChannelId: request.slackChannelId, - slackThreadTs: request.slackThreadTs, - computerUseHostId: args.computerUseHostId, - updatedAt: now, - }) - .onConflictDoUpdate({ - target: [ - slackOrgThreadSessions.connectionId, - slackOrgThreadSessions.slackChannelId, - slackOrgThreadSessions.slackThreadTs, - ], - set: { + const applied = + request.source === "chat" + ? await applyChatAuthorizationScope({ + db, + request, + userId: args.userId, computerUseHostId: args.computerUseHostId, - updatedAt: now, - }, - }); - signal.throwIfAborted(); - } else { + now, + }) + : request.source === "slack" + ? await applySlackAuthorizationScope({ + db, + request, + orgId: args.orgId, + userId: args.userId, + computerUseHostId: args.computerUseHostId, + now, + }) + : request.source === "teams" + ? await applyTeamsAuthorizationScope({ + db, + request, + orgId: args.orgId, + userId: args.userId, + computerUseHostId: args.computerUseHostId, + now, + }) + : false; + signal.throwIfAborted(); + + if (!applied) { return { status: "scope_not_found" }; } diff --git a/turbo/apps/api/src/signals/services/zero-computer-use.service.ts b/turbo/apps/api/src/signals/services/zero-computer-use.service.ts index 248c81c64f6..b072188e156 100644 --- a/turbo/apps/api/src/signals/services/zero-computer-use.service.ts +++ b/turbo/apps/api/src/signals/services/zero-computer-use.service.ts @@ -47,6 +47,7 @@ import { } from "@vm0/db/schema/computer-use-host"; import { chatThreads } from "@vm0/db/schema/chat-thread"; import { slackOrgThreadSessions } from "@vm0/db/schema/slack-org-thread-session"; +import { teamsOrgThreadSessions } from "@vm0/db/schema/teams-org-thread-session"; import { env } from "../../lib/env"; import { logger } from "../../lib/log"; @@ -297,6 +298,10 @@ async function clearComputerUseHostThreadBindings(params: { .update(slackOrgThreadSessions) .set({ computerUseHostId: null, updatedAt: now }) .where(eq(slackOrgThreadSessions.computerUseHostId, params.hostId)); + await params.tx + .update(teamsOrgThreadSessions) + .set({ computerUseHostId: null, updatedAt: now }) + .where(eq(teamsOrgThreadSessions.computerUseHostId, params.hostId)); } function isComputerUseWriteCommandKind( diff --git a/turbo/apps/api/src/signals/services/zero-teams-connect.service.ts b/turbo/apps/api/src/signals/services/zero-teams-connect.service.ts index 62d40937d60..da676435473 100644 --- a/turbo/apps/api/src/signals/services/zero-teams-connect.service.ts +++ b/turbo/apps/api/src/signals/services/zero-teams-connect.service.ts @@ -735,28 +735,14 @@ type BindTeamsInstallationResult = | { readonly kind: "not_found"; readonly message: string } | { readonly kind: "forbidden"; readonly message: string }; -function buildTeamsWelcomeCard(args: { - readonly agentName: string | undefined; -}): TeamsAdaptiveCard { +function buildTeamsWelcomeCard(): TeamsAdaptiveCard { return { type: "AdaptiveCard", version: "1.4", body: [ { type: "TextBlock", - text: "You're connected! Mention @Zero in a channel or send a DM to start chatting with your agent.", - wrap: true, - }, - { - type: "TextBlock", - text: "Hi! I'm Zero. I can connect you to AI agents to help with your tasks.", - wrap: true, - }, - { - type: "TextBlock", - text: args.agentName - ? `Workspace Agent\n- ${args.agentName}\n\nHow to Use\n- Just describe what you need help with` - : "No workspace agent configured yet.", + text: "You're connected! 🎉\nMention `@Zero` in any channel or send a DM to start chatting with your agent.", wrap: true, }, ], @@ -839,19 +825,12 @@ async function notifyTeamsConnect(args: { return; } - const defaultComposeId = await resolveDefaultComposeId(args.db, args.orgId); - args.signal.throwIfAborted(); - const agentName = defaultComposeId - ? await getTeamsAgentName(args.db, defaultComposeId) - : undefined; - args.signal.throwIfAborted(); - const sendResult = await sendTeamsMessageReply({ serviceUrl: args.serviceUrl, conversationId, tenantId: args.tenantId, text: "You're connected!", - card: buildTeamsWelcomeCard({ agentName }), + card: buildTeamsWelcomeCard(), signal: args.signal, }); args.signal.throwIfAborted(); diff --git a/turbo/apps/api/src/signals/services/zero-teams-dispatch.service.ts b/turbo/apps/api/src/signals/services/zero-teams-dispatch.service.ts index 1b3b2a49df1..981a8465a8d 100644 --- a/turbo/apps/api/src/signals/services/zero-teams-dispatch.service.ts +++ b/turbo/apps/api/src/signals/services/zero-teams-dispatch.service.ts @@ -1,7 +1,15 @@ import { randomBytes } from "node:crypto"; import { command } from "ccstate"; +import { + getVm0VisibleModels, + isSupportedRunModel, + type SupportedRunModel, +} from "@vm0/api-contracts/contracts/model-providers"; +import { isFeatureEnabled } from "@vm0/core/feature-switch"; +import { FeatureSwitchKey } from "@vm0/core/feature-switch-key"; import { agentSessions } from "@vm0/db/schema/agent-session"; +import { computerUseHosts } from "@vm0/db/schema/computer-use-host"; import { orgMetadata } from "@vm0/db/schema/org-metadata"; import { teamsOrgConnections } from "@vm0/db/schema/teams-org-connection"; import { teamsOrgInstallations } from "@vm0/db/schema/teams-org-installation"; @@ -9,7 +17,7 @@ import { teamsOrgThreadSessions } from "@vm0/db/schema/teams-org-thread-session" import { teamsUserAgentPreferences } from "@vm0/db/schema/teams-user-agent-preference"; import { zeroAgents } from "@vm0/db/schema/zero-agent"; import type { TeamsInboundActivity } from "@vm0/api-contracts/contracts/zero-teams-bot"; -import { and, eq, or } from "drizzle-orm"; +import { and, desc, eq, isNull, or } from "drizzle-orm"; import { convert } from "html-to-text"; import { env } from "../../lib/env"; @@ -20,8 +28,12 @@ import { fetchTeamsChannelMessage, fetchTeamsChannelMessageReplies, fetchTeamsChannelMessages, + sendTeamsReaction, + sendTeamsTypingActivity, + type TeamsAdaptiveCard, type TeamsGraphMessage, } from "../external/teams-bot-client"; +import { settle } from "../utils"; import { dispatchFailedRunCallbacks } from "./agent-run-callback.service"; import { formatIntegrationRunError$ } from "./integration-run-errors.service"; import { @@ -29,10 +41,17 @@ import { type IntegrationModelRoutePin, } from "./integration-model-route.service"; import { canReuseIntegrationSessionForModelRoute } from "./integration-session-model-compatibility.service"; +import type { ApiDispatchTimingCollector } from "./api-dispatch-timing.service"; +import { userFeatureSwitchOverrides } from "./feature-switches.service"; +import { listOrgModelPolicies$ } from "./zero-model-policy.service"; import { teamsOrgCallbackPayloadSchema, type TeamsOrgCallbackPayload, } from "./teams-org-callback-payload"; +import { + updateUserModelPreference$, + userModelPreference, +} from "./zero-user-data.service"; import { buildTeamsConnectUrlForActivity, disconnectTeamsConnection$, @@ -43,8 +62,18 @@ import { createZeroRun$ } from "./zero-runs-create.service"; const L = logger("TeamsDispatch"); const TEAMS_LOGIN_PROMPT_FALLBACK_TEXT = "Please connect your account to use Zero in this Teams workspace."; +const TEAMS_AGENT_PICKER_MAX_OPTIONS = 100; +const TEAMS_MODEL_PICKER_MAX_OPTIONS = 100; +const TEAMS_CARD_ACTION_KEY = "zeroTeamsAction"; +const TEAMS_AGENT_PICKER_ACTION = "switch_agent"; +const TEAMS_MODEL_PICKER_ACTION = "switch_model"; +const TEAMS_AGENT_PICKER_INPUT_ID = "selectedComposeId"; +const TEAMS_MODEL_PICKER_INPUT_ID = "selectedModel"; +const TEAMS_AGENT_PICKER_ORG_DEFAULT_VALUE = "__org_default__"; +const TEAMS_THINKING_REACTION_TYPE = "1f4ad_thoughtballoon"; type TeamsBotCommand = "help" | "connect" | "disconnect" | "switch" | "model"; +type TeamsCardAction = "switch_agent" | "switch_model"; type TeamsInstallation = typeof teamsOrgInstallations.$inferSelect; type BoundTeamsInstallation = TeamsInstallation & { readonly orgId: string }; @@ -66,6 +95,18 @@ interface TeamsAgent { readonly displayName: string | null; } +interface TeamsAgentPickerOption { + readonly composeId: string; + readonly name: string; + readonly displayName: string | null; +} + +interface TeamsModelPickerOption { + readonly model: SupportedRunModel; + readonly label: string; + readonly isDefault: boolean; +} + type EffectiveComposeResolution = | { readonly status: "resolved"; @@ -81,12 +122,18 @@ type ResolvedEffectiveCompose = Extract< { readonly status: "resolved" } >; +interface TeamsRunThreadContext { + readonly existingSessionId: string | undefined; + readonly computerUseHostId: string | undefined; +} + type TeamsMessageDispatchResult = | { readonly kind: "ignored" } | { readonly kind: "notice"; readonly replyText: string; readonly connectUrl?: string; + readonly card?: TeamsAdaptiveCard; } | { readonly kind: "accepted" | "queued"; @@ -114,10 +161,6 @@ function optionalLine( return normalized ? [`${label}: ${normalized}`] : []; } -function worksUrl(): string { - return `${env("APP_URL")}/works`; -} - function isTeamsBotCommand(value: string): value is TeamsBotCommand { return ( value === "help" || @@ -128,6 +171,46 @@ function isTeamsBotCommand(value: string): value is TeamsBotCommand { ); } +function stringValue( + value: Readonly> | null, + key: string, +): string | undefined { + const raw = value?.[key]; + return typeof raw === "string" && raw.length > 0 ? raw : undefined; +} + +function teamsCardAction( + value: Readonly> | null, +): TeamsCardAction | null { + const action = stringValue(value, TEAMS_CARD_ACTION_KEY); + if (action === TEAMS_AGENT_PICKER_ACTION) { + return "switch_agent"; + } + if (action === TEAMS_MODEL_PICKER_ACTION) { + return "switch_model"; + } + return null; +} + +function choiceLabel(value: string): string { + return value.slice(0, 80); +} + +function agentLabel(agent: TeamsAgent | TeamsAgentPickerOption): string { + return agent.displayName ?? agent.name; +} + +function modelLabel(option: TeamsModelPickerOption): string { + if (!option.isDefault) { + return choiceLabel(option.label); + } + const suffix = " (workspace default)"; + if (option.label.length + suffix.length <= 80) { + return `${option.label}${suffix}`; + } + return `${option.label.slice(0, 80 - suffix.length)}${suffix}`; +} + function parseTeamsBotCommand(prompt: string): TeamsBotCommand | null { const parts = prompt.trim().split(/\s+/u); const first = parts[0]?.toLowerCase() ?? ""; @@ -187,22 +270,119 @@ function disconnectedNotice(): TeamsMessageDispatchResult { }; } -function switchNotice( - agent: TeamsAgent | undefined, -): TeamsMessageDispatchResult { - const current = agent - ? `\n\nCurrent agent: \`${agent.displayName ?? agent.name}\`` - : ""; +function buildTeamsAgentPickerCard(args: { + readonly options: readonly TeamsAgentPickerOption[]; + readonly currentSelectedId: string | null; + readonly includeOrgDefault: boolean; + readonly orgDefaultName: string | null; +}): TeamsAdaptiveCard { + const orgDefaultLabel = args.orgDefaultName + ? `Use org default (${args.orgDefaultName})` + : "Use org default"; + const choices = [ + ...(args.includeOrgDefault + ? [ + { + title: choiceLabel(orgDefaultLabel), + value: TEAMS_AGENT_PICKER_ORG_DEFAULT_VALUE, + }, + ] + : []), + ...args.options.map((option) => { + return { + title: choiceLabel(agentLabel(option)), + value: option.composeId, + }; + }), + ]; + const currentChoice = args.currentSelectedId + ? choices.find((choice) => { + return choice.value === args.currentSelectedId; + }) + : undefined; + const initialValue = currentChoice?.value ?? choices[0]?.value; + return { - kind: "notice", - replyText: `Choose which agent responds to your Teams messages from [Works](${worksUrl()}).${current}`, + type: "AdaptiveCard", + version: "1.4", + body: [ + { + type: "TextBlock", + text: "Choose which agent should respond to your mentions and DMs. Only affects your own messages.", + wrap: true, + }, + { + type: "Input.ChoiceSet", + id: TEAMS_AGENT_PICKER_INPUT_ID, + label: "Agent", + style: "compact", + isMultiSelect: false, + ...(initialValue ? { value: initialValue } : {}), + choices, + }, + ], + actions: [ + { + type: "Action.Submit", + title: "Switch", + data: { [TEAMS_CARD_ACTION_KEY]: TEAMS_AGENT_PICKER_ACTION }, + }, + ], }; } -function modelNotice(): TeamsMessageDispatchResult { +function buildTeamsModelPickerCard(args: { + readonly options: readonly TeamsModelPickerOption[]; + readonly currentSelectedModel: string | null; +}): TeamsAdaptiveCard { + const choices = args.options.map((option) => { + return { + title: modelLabel(option), + value: option.model, + }; + }); + const defaultModel = args.options.find((option) => { + return option.isDefault; + })?.model; + const currentChoice = args.currentSelectedModel + ? choices.find((choice) => { + return choice.value === args.currentSelectedModel; + }) + : undefined; + const defaultChoice = defaultModel + ? choices.find((choice) => { + return choice.value === defaultModel; + }) + : undefined; + const initialValue = + currentChoice?.value ?? defaultChoice?.value ?? choices[0]?.value; + return { - kind: "notice", - replyText: `Choose the model for your Teams agent from [Works](${worksUrl()}).`, + type: "AdaptiveCard", + version: "1.4", + body: [ + { + type: "TextBlock", + text: "Choose your model. This only affects your own runs.", + wrap: true, + }, + { + type: "Input.ChoiceSet", + id: TEAMS_MODEL_PICKER_INPUT_ID, + label: "Model", + style: "compact", + isMultiSelect: false, + ...(initialValue ? { value: initialValue } : {}), + choices, + }, + ], + actions: [ + { + type: "Action.Submit", + title: "Switch", + data: { [TEAMS_CARD_ACTION_KEY]: TEAMS_MODEL_PICKER_ACTION }, + }, + ], }; } @@ -276,6 +456,67 @@ async function resolveDefaultComposeId( return metadata?.defaultAgentId ?? null; } +function buildTeamsDispatchErrorText(args: { + readonly errorText: string; + readonly logsUrl: string | undefined; + readonly footerText: string | undefined; +}): string { + return [ + args.errorText, + args.logsUrl ? `[Audit](${args.logsUrl})` : undefined, + args.footerText ? `_${args.footerText}_` : undefined, + ] + .filter((part): part is string => { + return Boolean(part); + }) + .join("\n\n"); +} + +async function sendTeamsRunStartIndicator(args: { + readonly activity: TeamsMessageActivity; + readonly signal: AbortSignal; +}): Promise { + const activityId = args.activity.activityId; + let mode: "typing" | "reaction"; + let indicator: + | ReturnType + | ReturnType; + if (isTeamsDirectMessage(args.activity) || !activityId) { + mode = "typing"; + indicator = sendTeamsTypingActivity({ + serviceUrl: args.activity.serviceUrl, + conversationId: args.activity.conversationId, + tenantId: args.activity.tenantId, + signal: args.signal, + }); + } else { + mode = "reaction"; + indicator = sendTeamsReaction({ + serviceUrl: args.activity.serviceUrl, + conversationId: args.activity.conversationId, + activityId, + tenantId: args.activity.tenantId, + reactionType: TEAMS_THINKING_REACTION_TYPE, + signal: args.signal, + }); + } + const result = await settle(indicator, args.signal); + const error = !result.ok + ? result.error + : result.value.kind === "teams-error" + ? result.value.error + : undefined; + if (error !== undefined) { + L.debug("Failed to send Teams run start indicator", { + tenantId: args.activity.tenantId, + conversationId: args.activity.conversationId, + activityId: args.activity.activityId, + mode, + error, + }); + } +} + async function getUserAgentPreference( db: Db, vm0UserId: string, @@ -294,6 +535,31 @@ async function getUserAgentPreference( return preference?.selectedComposeId ?? null; } +async function setUserAgentPreference(args: { + readonly db: Db; + readonly vm0UserId: string; + readonly orgId: string; + readonly composeId: string | null; +}): Promise { + await args.db + .insert(teamsUserAgentPreferences) + .values({ + vm0UserId: args.vm0UserId, + orgId: args.orgId, + selectedComposeId: args.composeId, + }) + .onConflictDoUpdate({ + target: [ + teamsUserAgentPreferences.vm0UserId, + teamsUserAgentPreferences.orgId, + ], + set: { + selectedComposeId: args.composeId, + updatedAt: nowDate(), + }, + }); +} + async function getWorkspaceAgent( db: Db, composeId: string, @@ -338,6 +604,37 @@ async function getVisibleWorkspaceAgent(args: { return agent; } +async function getVisibleAgentPickerOptions(args: { + readonly db: Db; + readonly orgId: string; + readonly userId: string; + readonly defaultComposeId: string | null; +}): Promise { + const rows = await args.db + .select({ + composeId: zeroAgents.id, + name: zeroAgents.name, + displayName: zeroAgents.displayName, + }) + .from(zeroAgents) + .where( + and( + eq(zeroAgents.orgId, args.orgId), + or( + eq(zeroAgents.visibility, "public"), + eq(zeroAgents.owner, args.userId), + ), + ), + ) + .orderBy(desc(zeroAgents.updatedAt)); + + return rows + .filter((agent) => { + return agent.composeId !== args.defaultComposeId; + }) + .slice(0, TEAMS_AGENT_PICKER_MAX_OPTIONS); +} + async function resolveEffectiveCompose(args: { readonly db: Db; readonly vm0UserId: string; @@ -388,17 +685,62 @@ async function resolveEffectiveCompose(args: { }; } +const teamsModelPickerState$ = command( + async ( + { get, set }, + orgId: string, + userId: string, + signal: AbortSignal, + ): Promise<{ + readonly enabled: boolean; + readonly options: readonly TeamsModelPickerOption[]; + readonly currentSelectedModel: string | null; + }> => { + const visibleModels = new Set(getVm0VisibleModels()); + const [policies, preference] = await Promise.all([ + set(listOrgModelPolicies$, { orgId, userId }, signal), + get(userModelPreference({ orgId, userId })), + ]); + signal.throwIfAborted(); + + return { + enabled: true, + options: policies.policies + .flatMap((policy) => { + if ( + !isSupportedRunModel(policy.model) || + !visibleModels.has(policy.model) || + policy.routeStatus !== "valid" + ) { + return []; + } + return { + model: policy.model, + label: policy.modelLabel, + isDefault: policy.isDefault, + }; + }) + .slice(0, TEAMS_MODEL_PICKER_MAX_OPTIONS), + currentSelectedModel: preference.selectedModel, + }; + }, +); + async function resolveCompatibleTeamsThreadSession(args: { readonly db: Db; readonly connectionId: string; readonly conversationId: string; readonly threadId: string; + readonly orgId: string; readonly userId: string; readonly composeId: string; readonly modelRoute: IntegrationModelRoutePin | undefined; -}): Promise { +}): Promise { const [threadSession] = await args.db - .select({ agentSessionId: teamsOrgThreadSessions.agentSessionId }) + .select({ + agentSessionId: teamsOrgThreadSessions.agentSessionId, + computerUseHostId: teamsOrgThreadSessions.computerUseHostId, + }) .from(teamsOrgThreadSessions) .where( and( @@ -408,16 +750,41 @@ async function resolveCompatibleTeamsThreadSession(args: { ), ) .limit(1); + const computerUseHostId = await resolveComputerUseHostForTeamsThread({ + db: args.db, + orgId: args.orgId, + userId: args.userId, + hostId: threadSession?.computerUseHostId ?? null, + }); + if (!threadSession?.agentSessionId) { - return undefined; + return { existingSessionId: undefined, computerUseHostId }; } + const existingSessionId = await resolveCompatibleTeamsAgentSession({ + db: args.db, + sessionId: threadSession.agentSessionId, + userId: args.userId, + composeId: args.composeId, + modelRoute: args.modelRoute, + }); + + return { existingSessionId, computerUseHostId }; +} + +async function resolveCompatibleTeamsAgentSession(args: { + readonly db: Db; + readonly sessionId: string; + readonly userId: string; + readonly composeId: string; + readonly modelRoute: IntegrationModelRoutePin | undefined; +}): Promise { const [agentSession] = await args.db .select({ agentComposeId: agentSessions.agentComposeId }) .from(agentSessions) .where( and( - eq(agentSessions.id, threadSession.agentSessionId), + eq(agentSessions.id, args.sessionId), eq(agentSessions.userId, args.userId), ), ) @@ -429,7 +796,7 @@ async function resolveCompatibleTeamsThreadSession(args: { if (args.modelRoute) { const canReuseSession = await canReuseIntegrationSessionForModelRoute({ db: args.db, - sessionId: threadSession.agentSessionId, + sessionId: args.sessionId, modelRoute: args.modelRoute, }); if (!canReuseSession) { @@ -437,7 +804,33 @@ async function resolveCompatibleTeamsThreadSession(args: { } } - return threadSession.agentSessionId; + return args.sessionId; +} + +async function resolveComputerUseHostForTeamsThread(args: { + readonly db: Db; + readonly orgId: string; + readonly userId: string; + readonly hostId: string | null; +}): Promise { + if (!args.hostId || !args.orgId) { + return undefined; + } + + const [host] = await args.db + .select({ id: computerUseHosts.id }) + .from(computerUseHosts) + .where( + and( + eq(computerUseHosts.id, args.hostId), + eq(computerUseHosts.orgId, args.orgId), + eq(computerUseHosts.userId, args.userId), + isNull(computerUseHosts.revokedAt), + ), + ) + .limit(1); + + return host?.id; } function formatTeamsSenderBlock(message: TeamsContextMessage): string { @@ -823,19 +1216,27 @@ function callbackPayload(args: { const runAgentForTeams$ = command( async ( - { set }, + { get, set }, args: { readonly activity: TeamsMessageActivity; readonly installation: BoundTeamsInstallation; readonly connection: TeamsConnection; readonly composeId: string; + readonly agentLabel: string; readonly sessionId: string | undefined; + readonly computerUseHostId: string | undefined; readonly threadContext: string; readonly apiStartTime: number; readonly modelRoute: IntegrationModelRoutePin | undefined; + readonly timing: ApiDispatchTimingCollector; }, signal: AbortSignal, ): Promise => { + args.timing.recordElapsed( + "api_dispatch_pre_create_zero_teams_create_run", + "nested", + nowDate().getTime(), + ); const result = await set( createZeroRun$, { @@ -870,7 +1271,9 @@ const runAgentForTeams$ = command( modelProviderCredentialScope: args.modelRoute?.modelProviderCredentialScope ?? undefined, selectedModelOverride: args.modelRoute?.selectedModel ?? undefined, + computerUseHostId: args.computerUseHostId, dispatchFailedCallbacks: dispatchFailedRunCallbacks, + timing: args.timing, callbacks: [ { internalKind: "teams:org", @@ -896,18 +1299,50 @@ const runAgentForTeams$ = command( }; } + const errorText = await set( + formatIntegrationRunError$, + { + orgId: args.installation.orgId, + userId: args.connection.vm0UserId, + code: result.body.error.code, + message: result.body.error.message, + }, + signal, + ); + signal.throwIfAborted(); + + const overrides = await get( + userFeatureSwitchOverrides( + args.installation.orgId, + args.connection.vm0UserId, + ), + ); + signal.throwIfAborted(); + const logsUrl = isFeatureEnabled(FeatureSwitchKey.ZeroDebug, { + userId: args.connection.vm0UserId, + orgId: args.installation.orgId, + overrides, + }) + ? `${env("APP_URL")}/activities` + : undefined; + const db = set(writeDb$); + const defaultComposeId = await resolveDefaultComposeId( + db, + args.installation.orgId, + ); + signal.throwIfAborted(); + const footerText = + args.composeId !== defaultComposeId + ? `Sent via ${args.agentLabel}` + : undefined; + return { kind: "failed", - replyText: await set( - formatIntegrationRunError$, - { - orgId: args.installation.orgId, - userId: args.connection.vm0UserId, - code: result.body.error.code, - message: result.body.error.message, - }, - signal, - ), + replyText: buildTeamsDispatchErrorText({ + errorText, + logsUrl, + footerText, + }), }; }, ); @@ -984,6 +1419,7 @@ const connectedCommandBeforeCompose$ = command( async ( { set }, args: { + readonly db: Db; readonly command: TeamsBotCommand | null; readonly installation: BoundTeamsInstallation; readonly connection: TeamsConnection; @@ -1021,8 +1457,83 @@ const connectedCommandBeforeCompose$ = command( signal.throwIfAborted(); return disconnectedNotice(); } - case "switch": - case "model": + case "switch": { + const defaultComposeId = await resolveDefaultComposeId( + args.db, + args.installation.orgId, + ); + signal.throwIfAborted(); + const options = await getVisibleAgentPickerOptions({ + db: args.db, + orgId: args.installation.orgId, + userId: args.connection.vm0UserId, + defaultComposeId, + }); + signal.throwIfAborted(); + const visibleDefaultAgent = defaultComposeId + ? await getVisibleWorkspaceAgent({ + db: args.db, + composeId: defaultComposeId, + orgId: args.installation.orgId, + userId: args.connection.vm0UserId, + }) + : undefined; + signal.throwIfAborted(); + if (!visibleDefaultAgent && options.length === 0) { + return { + kind: "notice", + replyText: "No agents are available to your Teams account.", + }; + } + const currentOverride = await getUserAgentPreference( + args.db, + args.connection.vm0UserId, + args.installation.orgId, + ); + signal.throwIfAborted(); + return { + kind: "notice", + replyText: + "Choose which agent should respond to your Teams messages.", + card: buildTeamsAgentPickerCard({ + options, + currentSelectedId: currentOverride, + includeOrgDefault: Boolean(visibleDefaultAgent), + orgDefaultName: visibleDefaultAgent + ? agentLabel(visibleDefaultAgent) + : null, + }), + }; + } + case "model": { + const picker = await set( + teamsModelPickerState$, + args.installation.orgId, + args.connection.vm0UserId, + signal, + ); + signal.throwIfAborted(); + if (!picker.enabled) { + return { + kind: "notice", + replyText: "Model switching is not available for this workspace.", + }; + } + if (picker.options.length === 0) { + return { + kind: "notice", + replyText: "No models are configured for this workspace.", + }; + } + return { + kind: "notice", + replyText: "Choose the model for your Teams agent.", + card: buildTeamsModelPickerCard({ + options: picker.options, + currentSelectedModel: picker.currentSelectedModel, + }), + }; + } case null: { return null; } @@ -1030,22 +1541,133 @@ const connectedCommandBeforeCompose$ = command( }, ); -function composeCommandNotice(args: { - readonly command: TeamsBotCommand | null; - readonly effectiveCompose: EffectiveComposeResolution; -}): TeamsMessageDispatchResult | null { - if (args.command === "switch") { - return switchNotice( - args.effectiveCompose.status === "resolved" - ? args.effectiveCompose.agent - : undefined, +const connectedTeamsCardAction$ = command( + async ( + { set }, + args: { + readonly action: TeamsCardAction; + readonly db: Db; + readonly activity: TeamsMessageActivity; + readonly installation: BoundTeamsInstallation; + readonly connection: TeamsConnection; + }, + signal: AbortSignal, + ): Promise => { + if (args.action === "switch_agent") { + const selected = stringValue( + args.activity.value, + TEAMS_AGENT_PICKER_INPUT_ID, + ); + if (!selected) { + return { + kind: "notice", + replyText: "Please choose an agent.", + }; + } + + if (selected === TEAMS_AGENT_PICKER_ORG_DEFAULT_VALUE) { + const defaultComposeId = await resolveDefaultComposeId( + args.db, + args.installation.orgId, + ); + signal.throwIfAborted(); + const visibleDefaultAgent = defaultComposeId + ? await getVisibleWorkspaceAgent({ + db: args.db, + composeId: defaultComposeId, + orgId: args.installation.orgId, + userId: args.connection.vm0UserId, + }) + : undefined; + signal.throwIfAborted(); + if (!visibleDefaultAgent) { + return { + kind: "notice", + replyText: "You don't have access to that agent.", + }; + } + await setUserAgentPreference({ + db: args.db, + vm0UserId: args.connection.vm0UserId, + orgId: args.installation.orgId, + composeId: null, + }); + signal.throwIfAborted(); + return { + kind: "notice", + replyText: `Switched to **${agentLabel(visibleDefaultAgent)}**.`, + }; + } + + const agent = await getVisibleWorkspaceAgent({ + db: args.db, + composeId: selected, + orgId: args.installation.orgId, + userId: args.connection.vm0UserId, + }); + signal.throwIfAborted(); + if (!agent || agent.id !== selected) { + return { + kind: "notice", + replyText: "You don't have access to that agent.", + }; + } + await setUserAgentPreference({ + db: args.db, + vm0UserId: args.connection.vm0UserId, + orgId: args.installation.orgId, + composeId: agent.id, + }); + signal.throwIfAborted(); + return { + kind: "notice", + replyText: `Switched to **${agentLabel(agent)}**.`, + }; + } + + const selected = stringValue( + args.activity.value, + TEAMS_MODEL_PICKER_INPUT_ID, ); - } - if (args.command === "model") { - return modelNotice(); - } - return null; -} + if (!selected) { + return { + kind: "notice", + replyText: "Please choose a model.", + }; + } + + const picker = await set( + teamsModelPickerState$, + args.installation.orgId, + args.connection.vm0UserId, + signal, + ); + signal.throwIfAborted(); + const option = picker.options.find((candidate) => { + return candidate.model === selected; + }); + if (!option) { + return { + kind: "notice", + replyText: "You don't have access to that model.", + }; + } + await set( + updateUserModelPreference$, + { + orgId: args.installation.orgId, + userId: args.connection.vm0UserId, + preference: { selectedModel: option.model }, + }, + signal, + ); + signal.throwIfAborted(); + return { + kind: "notice", + replyText: `Switched to **${option.label}**.`, + }; + }, +); const runResolvedTeamsAgentForActivity$ = command( async ( @@ -1058,9 +1680,16 @@ const runResolvedTeamsAgentForActivity$ = command( readonly connection: TeamsConnection; readonly effectiveCompose: ResolvedEffectiveCompose; readonly apiStartTime: number; + readonly timing: ApiDispatchTimingCollector; }, signal: AbortSignal, ): Promise => { + await sendTeamsRunStartIndicator({ + activity: args.activity, + signal, + }); + signal.throwIfAborted(); + const modelRoute = await set( resolveIntegrationModelRouteForUser$, { @@ -1071,11 +1700,12 @@ const runResolvedTeamsAgentForActivity$ = command( ); signal.throwIfAborted(); - const existingSessionId = await resolveCompatibleTeamsThreadSession({ + const runThreadContext = await resolveCompatibleTeamsThreadSession({ db: args.db, connectionId: args.connection.id, conversationId: args.activity.conversationId, threadId: args.activity.threadId, + orgId: args.installation.orgId, userId: args.connection.vm0UserId, composeId: args.effectiveCompose.composeId, modelRoute, @@ -1095,10 +1725,13 @@ const runResolvedTeamsAgentForActivity$ = command( installation: args.installation, connection: args.connection, composeId: args.effectiveCompose.composeId, - sessionId: existingSessionId, + agentLabel: agentLabel(args.effectiveCompose.agent), + sessionId: runThreadContext.existingSessionId, + computerUseHostId: runThreadContext.computerUseHostId, threadContext, apiStartTime: args.apiStartTime, modelRoute, + timing: args.timing, }, signal, ); @@ -1125,6 +1758,7 @@ export const dispatchTeamsMessageToAgent$ = command( readonly activity: TeamsInboundActivity; readonly installation?: TeamsInstallation | null; readonly apiStartTime: number; + readonly timing: ApiDispatchTimingCollector; }, signal: AbortSignal, ): Promise => { @@ -1133,18 +1767,19 @@ export const dispatchTeamsMessageToAgent$ = command( return { kind: "ignored" }; } - if (!shouldDispatchTeamsMessage(activity)) { + const cardAction = teamsCardAction(activity.value); + if (!shouldDispatchTeamsMessage(activity) && !cardAction) { return { kind: "ignored" }; } const prompt = activity.text.trim(); - if (!prompt) { + if (!prompt && !cardAction) { return { kind: "notice", replyText: "Please include a message for Zero.", }; } - const command = parseTeamsBotCommand(prompt); + const command = cardAction ? null : parseTeamsBotCommand(prompt); const db = set(writeDb$); const installation = @@ -1173,9 +1808,23 @@ export const dispatchTeamsMessageToAgent$ = command( return missingConnectionNotice({ command, activity, installation }); } + if (cardAction) { + return set( + connectedTeamsCardAction$, + { + action: cardAction, + db, + activity, + installation: boundInstallation, + connection, + }, + signal, + ); + } + const commandResult = await set( connectedCommandBeforeCompose$, - { command, installation: boundInstallation, connection }, + { db, command, installation: boundInstallation, connection }, signal, ); signal.throwIfAborted(); @@ -1190,14 +1839,6 @@ export const dispatchTeamsMessageToAgent$ = command( }); signal.throwIfAborted(); - const composeCommandResult = composeCommandNotice({ - command, - effectiveCompose, - }); - if (composeCommandResult) { - return composeCommandResult; - } - if (effectiveCompose.status !== "resolved") { return composeResolutionNotice(effectiveCompose.status); } @@ -1212,6 +1853,7 @@ export const dispatchTeamsMessageToAgent$ = command( connection, effectiveCompose, apiStartTime: args.apiStartTime, + timing: args.timing, }, signal, ); diff --git a/turbo/apps/platform/src/views/computer-use-authorization/__tests__/computer-use-authorization-page.test.tsx b/turbo/apps/platform/src/views/computer-use-authorization/__tests__/computer-use-authorization-page.test.tsx index 14cba49f8e2..888b43503d1 100644 --- a/turbo/apps/platform/src/views/computer-use-authorization/__tests__/computer-use-authorization-page.test.tsx +++ b/turbo/apps/platform/src/views/computer-use-authorization/__tests__/computer-use-authorization-page.test.tsx @@ -200,6 +200,39 @@ describe("computer use authorization page", () => { expect(remainingAuthorizeButtons[0]).toBeDisabled(); }); + it("labels Teams authorization requests", async () => { + context.mocks.api( + zeroComputerUseAuthorizationRequestsContract.get, + ({ respond }) => { + return respond(200, { + source: "teams", + expiresAt: "2026-06-25T12:00:00Z", + completedAt: null, + computerUseHostId: null, + hosts: [ + computerUseHost({ + id: "00000000-0000-4000-a000-000000000005", + displayName: "Teams Mac", + status: "online", + }), + ], + }); + }, + ); + + detachedSetupPage({ + context, + path: "/computer-use/authorize/vm0_computer_use_authorization_request_teams", + }); + + await expect( + screen.findByText( + "Choose an online computer for Zero to use in this Teams thread.", + ), + ).resolves.toBeInTheDocument(); + expect(screen.getByText("Teams Mac")).toBeInTheDocument(); + }); + it("shows desktop guidance when there are no online hosts", async () => { context.mocks.api( zeroComputerUseAuthorizationRequestsContract.get, diff --git a/turbo/apps/platform/src/views/computer-use-authorization/computer-use-authorization-page.tsx b/turbo/apps/platform/src/views/computer-use-authorization/computer-use-authorization-page.tsx index 1a3a177e0f3..c9bc0d73e64 100644 --- a/turbo/apps/platform/src/views/computer-use-authorization/computer-use-authorization-page.tsx +++ b/turbo/apps/platform/src/views/computer-use-authorization/computer-use-authorization-page.tsx @@ -7,7 +7,10 @@ import { IconDownload, IconLoader2, } from "@tabler/icons-react"; -import type { ComputerUseHost } from "@vm0/api-contracts/contracts/zero-computer-use"; +import type { + ComputerUseAuthorizationSource, + ComputerUseHost, +} from "@vm0/api-contracts/contracts/zero-computer-use"; import { FeatureSwitchKey } from "@vm0/connectors/feature-switch-key"; import { Button } from "@vm0/ui/components/ui/button"; import { @@ -35,8 +38,15 @@ function formatTime(value: string): string { }); } -function sourceLabel(source: "chat" | "slack"): string { - return source === "slack" ? "this Slack thread" : "this chat thread"; +function sourceLabel(source: ComputerUseAuthorizationSource): string { + switch (source) { + case "chat": + return "this chat thread"; + case "slack": + return "this Slack thread"; + case "teams": + return "this Teams thread"; + } } function HostOption({ diff --git a/turbo/packages/api-contracts/src/contracts/test-computer-use-state.ts b/turbo/packages/api-contracts/src/contracts/test-computer-use-state.ts index 7ea7b5ccad5..30421e79204 100644 --- a/turbo/packages/api-contracts/src/contracts/test-computer-use-state.ts +++ b/turbo/packages/api-contracts/src/contracts/test-computer-use-state.ts @@ -11,7 +11,7 @@ const testComputerUseStateErrorSchema = z.object({ export const testComputerUseStatePostBodySchema = z.object({ user_id: z.string().optional(), org_id: z.string().optional(), - trigger_source: z.enum(["web", "slack"]).optional(), + trigger_source: z.enum(["web", "slack", "teams"]).optional(), }); export const testComputerUseStatePostResponseSchema = z.object({ @@ -27,10 +27,17 @@ export const testComputerUseStatePostResponseSchema = z.object({ thread_ts: z.string(), }) .nullable(), + teams: z + .object({ + connection_id: z.string(), + conversation_id: z.string(), + thread_id: z.string(), + }) + .nullable(), }); export const testComputerUseStateGetResponseSchema = z.object({ - source: z.enum(["web", "slack"]).nullable(), + source: z.enum(["web", "slack", "teams"]).nullable(), computer_use_host_id: z.string().nullable(), }); diff --git a/turbo/packages/api-contracts/src/contracts/zero-computer-use.ts b/turbo/packages/api-contracts/src/contracts/zero-computer-use.ts index a5fb21d2387..b81fd9a01e2 100644 --- a/turbo/packages/api-contracts/src/contracts/zero-computer-use.ts +++ b/turbo/packages/api-contracts/src/contracts/zero-computer-use.ts @@ -455,7 +455,11 @@ export const computerUseHostDeleteResponseSchema = z.object({ ok: z.literal(true), }); -export const computerUseAuthorizationSourceSchema = z.enum(["chat", "slack"]); +export const computerUseAuthorizationSourceSchema = z.enum([ + "chat", + "slack", + "teams", +]); const computerUseAuthorizationRequestCreateBodySchema = z.object({}); diff --git a/turbo/packages/api-contracts/src/contracts/zero-teams-bot.ts b/turbo/packages/api-contracts/src/contracts/zero-teams-bot.ts index ce03cd1cb8c..0b947532850 100644 --- a/turbo/packages/api-contracts/src/contracts/zero-teams-bot.ts +++ b/turbo/packages/api-contracts/src/contracts/zero-teams-bot.ts @@ -35,6 +35,7 @@ export const teamsInboundMessageActivitySchema = teamsActivityBaseSchema.extend( recipient: teamsActorSchema.nullable(), rawText: z.string(), text: z.string(), + value: z.record(z.string(), z.unknown()).nullable(), mentionsRecipient: z.boolean(), }, ); @@ -82,6 +83,7 @@ const teamsBotDispatchResponseSchema = z.discriminatedUnion("kind", [ kind: z.literal("notice"), replyText: z.string(), connectUrl: z.string().optional(), + card: z.record(z.string(), z.unknown()).optional(), }), z.object({ kind: z.literal("accepted"), diff --git a/turbo/packages/db/src/migrations/0565_eager_iron_monger.sql b/turbo/packages/db/src/migrations/0565_eager_iron_monger.sql new file mode 100644 index 00000000000..f49d73da19f --- /dev/null +++ b/turbo/packages/db/src/migrations/0565_eager_iron_monger.sql @@ -0,0 +1,38 @@ +ALTER TABLE "computer_use_authorization_requests" DROP CONSTRAINT "computer_use_auth_requests_source_check";--> statement-breakpoint +ALTER TABLE "computer_use_authorization_requests" DROP CONSTRAINT "computer_use_auth_requests_scope_check";--> statement-breakpoint +ALTER TABLE "org_metadata" ALTER COLUMN "tier" SET DEFAULT 'limited-free-1';--> statement-breakpoint +ALTER TABLE "computer_use_authorization_requests" ADD COLUMN "teams_connection_id" uuid;--> statement-breakpoint +ALTER TABLE "computer_use_authorization_requests" ADD COLUMN "teams_conversation_id" text;--> statement-breakpoint +ALTER TABLE "computer_use_authorization_requests" ADD COLUMN "teams_thread_id" text;--> statement-breakpoint +ALTER TABLE "teams_org_thread_sessions" ADD COLUMN "computer_use_host_id" uuid;--> statement-breakpoint +ALTER TABLE "teams_org_thread_sessions" ADD CONSTRAINT "teams_org_thread_sessions_computer_use_host_id_computer_use_hosts_id_fk" FOREIGN KEY ("computer_use_host_id") REFERENCES "public"."computer_use_hosts"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "idx_teams_org_thread_sessions_computer_use_host" ON "teams_org_thread_sessions" USING btree ("computer_use_host_id");--> statement-breakpoint +ALTER TABLE "computer_use_authorization_requests" ADD CONSTRAINT "computer_use_auth_requests_source_check" CHECK (source IN ('chat', 'slack', 'teams'));--> statement-breakpoint +ALTER TABLE "computer_use_authorization_requests" ADD CONSTRAINT "computer_use_auth_requests_scope_check" CHECK (( + source = 'chat' + AND chat_thread_id IS NOT NULL + AND slack_connection_id IS NULL + AND slack_channel_id IS NULL + AND slack_thread_ts IS NULL + AND teams_connection_id IS NULL + AND teams_conversation_id IS NULL + AND teams_thread_id IS NULL + ) OR ( + source = 'slack' + AND chat_thread_id IS NULL + AND slack_connection_id IS NOT NULL + AND slack_channel_id IS NOT NULL + AND slack_thread_ts IS NOT NULL + AND teams_connection_id IS NULL + AND teams_conversation_id IS NULL + AND teams_thread_id IS NULL + ) OR ( + source = 'teams' + AND chat_thread_id IS NULL + AND slack_connection_id IS NULL + AND slack_channel_id IS NULL + AND slack_thread_ts IS NULL + AND teams_connection_id IS NOT NULL + AND teams_conversation_id IS NOT NULL + AND teams_thread_id IS NOT NULL + )); \ No newline at end of file diff --git a/turbo/packages/db/src/migrations/meta/0565_snapshot.json b/turbo/packages/db/src/migrations/meta/0565_snapshot.json new file mode 100644 index 00000000000..d637402e8c3 --- /dev/null +++ b/turbo/packages/db/src/migrations/meta/0565_snapshot.json @@ -0,0 +1,19827 @@ +{ + "id": "a38ff57a-e93b-4f66-9e25-a5bdda2570c8", + "prevId": "491deef3-d736-41f5-8923-3d1f438204e7", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.agent_compose_versions": { + "name": "agent_compose_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": true, + "notNull": true + }, + "compose_id": { + "name": "compose_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_agent_compose_versions_compose_id": { + "name": "idx_agent_compose_versions_compose_id", + "columns": [ + { + "expression": "compose_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_compose_versions_compose_id_agent_composes_id_fk": { + "name": "agent_compose_versions_compose_id_agent_composes_id_fk", + "tableFrom": "agent_compose_versions", + "tableTo": "agent_composes", + "columnsFrom": [ + "compose_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_composes": { + "name": "agent_composes", + "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 + }, + "name": { + "name": "name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "head_version_id": { + "name": "head_version_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_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": { + "idx_agent_composes_org": { + "name": "idx_agent_composes_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agent_composes_org_name": { + "name": "idx_agent_composes_org_name", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_run_callbacks": { + "name": "agent_run_callbacks", + "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 + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "internal_kind": { + "name": "internal_kind", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "encrypted_secret": { + "name": "encrypted_secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_agent_run_callbacks_run_id": { + "name": "idx_agent_run_callbacks_run_id", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agent_run_callbacks_pending": { + "name": "idx_agent_run_callbacks_pending", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "status = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_run_callbacks_run_id_agent_runs_id_fk": { + "name": "agent_run_callbacks_run_id_agent_runs_id_fk", + "tableFrom": "agent_run_callbacks", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_run_custom_connector_auth_refs": { + "name": "agent_run_custom_connector_auth_refs", + "schema": "", + "columns": { + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "secret_name": { + "name": "secret_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "encrypted_value": { + "name": "encrypted_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": true, + "default": "now()" + } + }, + "indexes": { + "idx_agent_run_custom_connector_auth_refs_expires": { + "name": "idx_agent_run_custom_connector_auth_refs_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_run_custom_connector_auth_refs_run_id_agent_runs_id_fk": { + "name": "agent_run_custom_connector_auth_refs_run_id_agent_runs_id_fk", + "tableFrom": "agent_run_custom_connector_auth_refs", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "agent_run_custom_connector_auth_refs_run_id_secret_name_pk": { + "name": "agent_run_custom_connector_auth_refs_run_id_secret_name_pk", + "columns": [ + "run_id", + "secret_name" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_run_queue": { + "name": "agent_run_queue", + "schema": "", + "columns": { + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_params": { + "name": "encrypted_params", + "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 + } + }, + "indexes": { + "agent_run_queue_user_created_idx": { + "name": "agent_run_queue_user_created_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": {} + }, + "agent_run_queue_org_created_idx": { + "name": "agent_run_queue_org_created_idx", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_run_queue_expires_at_idx": { + "name": "agent_run_queue_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_run_queue_run_id_agent_runs_id_fk": { + "name": "agent_run_queue_run_id_agent_runs_id_fk", + "tableFrom": "agent_run_queue", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_runs": { + "name": "agent_runs", + "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 + }, + "agent_compose_version_id": { + "name": "agent_compose_version_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "resumed_from_checkpoint_id": { + "name": "resumed_from_checkpoint_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "continued_from_session_id": { + "name": "continued_from_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "append_system_prompt": { + "name": "append_system_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vars": { + "name": "vars", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "secret_names": { + "name": "secret_names", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "additional_volumes": { + "name": "additional_volumes", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "sandbox_id": { + "name": "sandbox_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "sandbox_reuse_result": { + "name": "sandbox_reuse_result", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_event_sequence": { + "name": "last_event_sequence", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "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 + }, + "last_heartbeat_at": { + "name": "last_heartbeat_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "runner_group": { + "name": "runner_group", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_agent_runs_user_created": { + "name": "idx_agent_runs_user_created", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agent_runs_org": { + "name": "idx_agent_runs_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agent_runs_status_heartbeat": { + "name": "idx_agent_runs_status_heartbeat", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_heartbeat_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agent_runs_running_heartbeat": { + "name": "idx_agent_runs_running_heartbeat", + "columns": [ + { + "expression": "last_heartbeat_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "status = 'running'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agent_runs_org_status_created": { + "name": "idx_agent_runs_org_status_created", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agent_runs_session": { + "name": "idx_agent_runs_session", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agent_runs_completed_org_user": { + "name": "idx_agent_runs_completed_org_user", + "columns": [ + { + "expression": "completed_at", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"agent_runs\".\"completed_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_runs_agent_compose_version_id_agent_compose_versions_id_fk": { + "name": "agent_runs_agent_compose_version_id_agent_compose_versions_id_fk", + "tableFrom": "agent_runs", + "tableTo": "agent_compose_versions", + "columnsFrom": [ + "agent_compose_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "agent_runs_session_id_agent_sessions_id_fk": { + "name": "agent_runs_session_id_agent_sessions_id_fk", + "tableFrom": "agent_runs", + "tableTo": "agent_sessions", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_sessions": { + "name": "agent_sessions", + "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 + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_compose_id": { + "name": "agent_compose_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "artifacts": { + "name": "artifacts", + "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": { + "idx_agent_sessions_user_compose": { + "name": "idx_agent_sessions_user_compose", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_compose_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agent_sessions_org": { + "name": "idx_agent_sessions_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_sessions_agent_compose_id_agent_composes_id_fk": { + "name": "agent_sessions_agent_compose_id_agent_composes_id_fk", + "tableFrom": "agent_sessions", + "tableTo": "agent_composes", + "columnsFrom": [ + "agent_compose_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_sessions_conversation_id_conversations_id_fk": { + "name": "agent_sessions_conversation_id_conversations_id_fk", + "tableFrom": "agent_sessions", + "tableTo": "conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.conversations": { + "name": "conversations", + "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 + }, + "cli_agent_type": { + "name": "cli_agent_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "cli_agent_session_id": { + "name": "cli_agent_session_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "cli_agent_session_history": { + "name": "cli_agent_session_history", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cli_agent_session_history_hash": { + "name": "cli_agent_session_history_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "conversations_run_id_agent_runs_id_fk": { + "name": "conversations_run_id_agent_runs_id_fk", + "tableFrom": "conversations", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "conversations_run_id_unique": { + "name": "conversations_run_id_unique", + "nullsNotDistinct": false, + "columns": [ + "run_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agentphone_messages": { + "name": "agentphone_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "webhook_id": { + "name": "webhook_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "agentphone_message_id": { + "name": "agentphone_message_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "agentphone_agent_id": { + "name": "agentphone_agent_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "agentphone_user_link_id": { + "name": "agentphone_user_link_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "phone_handle": { + "name": "phone_handle", + "type": "varchar(254)", + "primaryKey": false, + "notNull": true + }, + "from_number": { + "name": "from_number", + "type": "varchar(254)", + "primaryKey": false, + "notNull": true + }, + "to_number": { + "name": "to_number", + "type": "varchar(254)", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "media_url": { + "name": "media_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_agentphone_messages_agentphone_message": { + "name": "idx_agentphone_messages_agentphone_message", + "columns": [ + { + "expression": "agentphone_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agentphone_messages_webhook_id": { + "name": "idx_agentphone_messages_webhook_id", + "columns": [ + { + "expression": "webhook_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "webhook_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agentphone_messages_handle_created": { + "name": "idx_agentphone_messages_handle_created", + "columns": [ + { + "expression": "phone_handle", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agentphone_messages_user_link": { + "name": "idx_agentphone_messages_user_link", + "columns": [ + { + "expression": "agentphone_user_link_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agentphone_messages_agentphone_user_link_id_agentphone_user_links_id_fk": { + "name": "agentphone_messages_agentphone_user_link_id_agentphone_user_links_id_fk", + "tableFrom": "agentphone_messages", + "tableTo": "agentphone_user_links", + "columnsFrom": [ + "agentphone_user_link_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agentphone_thread_sessions": { + "name": "agentphone_thread_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "agentphone_user_link_id": { + "name": "agentphone_user_link_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "conversation_id": { + "name": "conversation_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "root_message_id": { + "name": "root_message_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "agent_session_id": { + "name": "agent_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "last_processed_message_id": { + "name": "last_processed_message_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_agentphone_thread_sessions_link_root": { + "name": "idx_agentphone_thread_sessions_link_root", + "columns": [ + { + "expression": "agentphone_user_link_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "root_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agentphone_thread_sessions_user_link": { + "name": "idx_agentphone_thread_sessions_user_link", + "columns": [ + { + "expression": "agentphone_user_link_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agentphone_thread_sessions_agentphone_user_link_id_agentphone_user_links_id_fk": { + "name": "agentphone_thread_sessions_agentphone_user_link_id_agentphone_user_links_id_fk", + "tableFrom": "agentphone_thread_sessions", + "tableTo": "agentphone_user_links", + "columnsFrom": [ + "agentphone_user_link_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agentphone_thread_sessions_agent_session_id_agent_sessions_id_fk": { + "name": "agentphone_thread_sessions_agent_session_id_agent_sessions_id_fk", + "tableFrom": "agentphone_thread_sessions", + "tableTo": "agent_sessions", + "columnsFrom": [ + "agent_session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agentphone_user_agent_preferences": { + "name": "agentphone_user_agent_preferences", + "schema": "", + "columns": { + "vm0_user_id": { + "name": "vm0_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selected_compose_id": { + "name": "selected_compose_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "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": { + "agentphone_user_agent_preferences_selected_compose_id_agent_composes_id_fk": { + "name": "agentphone_user_agent_preferences_selected_compose_id_agent_composes_id_fk", + "tableFrom": "agentphone_user_agent_preferences", + "tableTo": "agent_composes", + "columnsFrom": [ + "selected_compose_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "agentphone_user_agent_preferences_pkey": { + "name": "agentphone_user_agent_preferences_pkey", + "columns": [ + "vm0_user_id", + "org_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agentphone_user_links": { + "name": "agentphone_user_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "phone_handle": { + "name": "phone_handle", + "type": "varchar(254)", + "primaryKey": false, + "notNull": true + }, + "vm0_user_id": { + "name": "vm0_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_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": { + "idx_agentphone_user_links_phone_handle": { + "name": "idx_agentphone_user_links_phone_handle", + "columns": [ + { + "expression": "phone_handle", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agentphone_user_links_vm0_org": { + "name": "idx_agentphone_user_links_vm0_org", + "columns": [ + { + "expression": "vm0_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_agentphone_user_links_org": { + "name": "idx_agentphone_user_links_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agentphone_verification_send_cooldowns": { + "name": "agentphone_verification_send_cooldowns", + "schema": "", + "columns": { + "scope": { + "name": "scope", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "scope_key": { + "name": "scope_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_sent_at": { + "name": "last_sent_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "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": { + "agentphone_verification_send_cooldowns_pkey": { + "name": "agentphone_verification_send_cooldowns_pkey", + "columns": [ + "scope", + "scope_key" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.archived_task_runs": { + "name": "archived_task_runs", + "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 + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "task_id": { + "name": "task_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "task_type": { + "name": "task_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "archived_run_id": { + "name": "archived_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_archived_task_runs_unique": { + "name": "idx_archived_task_runs_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "task_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "task_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.banking_access_audit_events": { + "name": "banking_access_audit_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'finicity'" + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "failure_code": { + "name": "failure_code", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_banking_access_audit_org_user": { + "name": "idx_banking_access_audit_org_user", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_banking_access_audit_run": { + "name": "idx_banking_access_audit_run", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_banking_access_audit_created": { + "name": "idx_banking_access_audit_created", + "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.banking_accounts": { + "name": "banking_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "institution_name": { + "name": "institution_name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "account_type": { + "name": "account_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "account_number_last4": { + "name": "account_number_last4", + "type": "varchar(8)", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "metadata": { + "name": "metadata", + "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": { + "idx_banking_accounts_connection_provider_account": { + "name": "idx_banking_accounts_connection_provider_account", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_banking_accounts_org_user": { + "name": "idx_banking_accounts_org_user", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "banking_accounts_connection_id_banking_connections_id_fk": { + "name": "banking_accounts_connection_id_banking_connections_id_fk", + "tableFrom": "banking_accounts", + "tableTo": "banking_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.banking_agent_enablements": { + "name": "banking_agent_enablements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "account_provider_ids": { + "name": "account_provider_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "operation_scopes": { + "name": "operation_scopes", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"accounts.read\",\"balances.read\",\"transactions.read\"]'::jsonb" + }, + "allow_automation_runs": { + "name": "allow_automation_runs", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_banking_agent_enablements_unique": { + "name": "idx_banking_agent_enablements_unique", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_banking_agent_enablements_agent_user": { + "name": "idx_banking_agent_enablements_agent_user", + "columns": [ + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "banking_agent_enablements_agent_id_zero_agents_id_fk": { + "name": "banking_agent_enablements_agent_id_zero_agents_id_fk", + "tableFrom": "banking_agent_enablements", + "tableTo": "zero_agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "banking_agent_enablements_connection_id_banking_connections_id_fk": { + "name": "banking_agent_enablements_connection_id_banking_connections_id_fk", + "tableFrom": "banking_agent_enablements", + "tableTo": "banking_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.banking_connections": { + "name": "banking_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'finicity'" + }, + "provider_customer_id": { + "name": "provider_customer_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "consent_expires_at": { + "name": "consent_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "repair_required_at": { + "name": "repair_required_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "audit_metadata": { + "name": "audit_metadata", + "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": { + "idx_banking_connections_owner_provider": { + "name": "idx_banking_connections_owner_provider", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_banking_connections_org_user": { + "name": "idx_banking_connections_org_user", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.blobs": { + "name": "blobs", + "schema": "", + "columns": { + "hash": { + "name": "hash", + "type": "varchar(64)", + "primaryKey": true, + "notNull": true + }, + "raw_size": { + "name": "raw_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "encoding": { + "name": "encoding", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "encoded_size": { + "name": "encoded_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "ref_count": { + "name": "ref_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_blobs_ref_count": { + "name": "idx_blobs_ref_count", + "columns": [ + { + "expression": "ref_count", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.built_in_generation_jobs": { + "name": "built_in_generation_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "type": { + "name": "type", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "request": { + "name": "request", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_built_in_generation_jobs_user_created": { + "name": "idx_built_in_generation_jobs_user_created", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_built_in_generation_jobs_org_status": { + "name": "idx_built_in_generation_jobs_org_status", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_built_in_generation_jobs_run": { + "name": "idx_built_in_generation_jobs_run", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "built_in_generation_jobs_run_id_agent_runs_id_fk": { + "name": "built_in_generation_jobs_run_id_agent_runs_id_fk", + "tableFrom": "built_in_generation_jobs", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_messages": { + "name": "chat_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_thread_id": { + "name": "chat_thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "usage_payload": { + "name": "usage_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "revokes_message_id": { + "name": "revokes_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "interrupts_run_id": { + "name": "interrupts_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "run_group_id": { + "name": "run_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "automation_id": { + "name": "automation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "automation_title": { + "name": "automation_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "automation_snapshot": { + "name": "automation_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thinking": { + "name": "thinking", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "run_lifecycle_event": { + "name": "run_lifecycle_event", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sequence_number": { + "name": "sequence_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "run_event_id": { + "name": "run_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "goal_event": { + "name": "goal_event", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "goal_snapshot": { + "name": "goal_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "attach_files": { + "name": "attach_files", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "attach_file_metadata": { + "name": "attach_file_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "generation_template": { + "name": "generation_template", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "recommended_followups": { + "name": "recommended_followups", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_chat_messages_thread_created": { + "name": "idx_chat_messages_thread_created", + "columns": [ + { + "expression": "chat_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_chat_messages_thread_run_finish_created": { + "name": "idx_chat_messages_thread_run_finish_created", + "columns": [ + { + "expression": "chat_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"chat_messages\".\"run_lifecycle_event\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_chat_messages_run_id": { + "name": "idx_chat_messages_run_id", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_usage_run_id_idx": { + "name": "chat_messages_usage_run_id_idx", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"chat_messages\".\"usage_payload\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_revokes_message_id_unique": { + "name": "chat_messages_revokes_message_id_unique", + "columns": [ + { + "expression": "revokes_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_interrupts_run_id_unique": { + "name": "chat_messages_interrupts_run_id_unique", + "columns": [ + { + "expression": "interrupts_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_chat_messages_run_group_id": { + "name": "idx_chat_messages_run_group_id", + "columns": [ + { + "expression": "run_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"chat_messages\".\"run_group_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_run_seq_unique": { + "name": "chat_messages_run_seq_unique", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_run_lifecycle_unique": { + "name": "chat_messages_run_lifecycle_unique", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat_messages\".\"run_lifecycle_event\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "chat_messages_run_thinking_unique": { + "name": "chat_messages_run_thinking_unique", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"chat_messages\".\"thinking\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_messages_chat_thread_id_chat_threads_id_fk": { + "name": "chat_messages_chat_thread_id_chat_threads_id_fk", + "tableFrom": "chat_messages", + "tableTo": "chat_threads", + "columnsFrom": [ + "chat_thread_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_messages_run_id_agent_runs_id_fk": { + "name": "chat_messages_run_id_agent_runs_id_fk", + "tableFrom": "chat_messages", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_messages_revokes_message_id_chat_messages_id_fk": { + "name": "chat_messages_revokes_message_id_chat_messages_id_fk", + "tableFrom": "chat_messages", + "tableTo": "chat_messages", + "columnsFrom": [ + "revokes_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "chat_messages_interrupts_run_id_agent_runs_id_fk": { + "name": "chat_messages_interrupts_run_id_agent_runs_id_fk", + "tableFrom": "chat_messages", + "tableTo": "agent_runs", + "columnsFrom": [ + "interrupts_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_output_materializations": { + "name": "chat_output_materializations", + "schema": "", + "columns": { + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "processed_through_sequence": { + "name": "processed_through_sequence", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": -1 + }, + "latest_result_sequence": { + "name": "latest_result_sequence", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_output_materializations_run_id_agent_runs_id_fk": { + "name": "chat_output_materializations_run_id_agent_runs_id_fk", + "tableFrom": "chat_output_materializations", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_thread_events": { + "name": "chat_thread_events", + "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 + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_thread_id": { + "name": "chat_thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "chat_thread_event_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "agent_compose_id": { + "name": "agent_compose_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "selected_model": { + "name": "selected_model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_chat_thread_events_user_org_created": { + "name": "idx_chat_thread_events_user_org_created", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "org_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": {} + }, + "idx_chat_thread_events_thread_created": { + "name": "idx_chat_thread_events_thread_created", + "columns": [ + { + "expression": "chat_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_thread_snapshots": { + "name": "chat_thread_snapshots", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "latest_event_id": { + "name": "latest_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "chat_threads": { + "name": "chat_threads", + "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": {}, + "compositePrimaryKeys": { + "chat_thread_snapshots_user_id_org_id_pk": { + "name": "chat_thread_snapshots_user_id_org_id_pk", + "columns": [ + "user_id", + "org_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_threads": { + "name": "chat_threads", + "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 + }, + "agent_compose_id": { + "name": "agent_compose_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_schedule_run_id": { + "name": "source_schedule_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "draft_content": { + "name": "draft_content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_attachments": { + "name": "draft_attachments", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "last_read_at": { + "name": "last_read_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "model_provider_id": { + "name": "model_provider_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "model_provider_type": { + "name": "model_provider_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "model_provider_credential_scope": { + "name": "model_provider_credential_scope", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "selected_model": { + "name": "selected_model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "codex_service_tier": { + "name": "codex_service_tier", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "generation_template": { + "name": "generation_template", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "computer_use_host_id": { + "name": "computer_use_host_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "pinned_at": { + "name": "pinned_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "renamed_at": { + "name": "renamed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_message_at": { + "name": "last_message_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_chat_threads_user_compose_updated": { + "name": "idx_chat_threads_user_compose_updated", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_compose_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_chat_threads_user_last_read": { + "name": "idx_chat_threads_user_last_read", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_read_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_chat_threads_user_compose_pinned": { + "name": "idx_chat_threads_user_compose_pinned", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_compose_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"chat_threads\".\"pinned_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_chat_threads_user_compose_last_message": { + "name": "idx_chat_threads_user_compose_last_message", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_compose_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_message_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_threads_agent_compose_id_agent_composes_id_fk": { + "name": "chat_threads_agent_compose_id_agent_composes_id_fk", + "tableFrom": "chat_threads", + "tableTo": "agent_composes", + "columnsFrom": [ + "agent_compose_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_threads_computer_use_host_id_computer_use_hosts_id_fk": { + "name": "chat_threads_computer_use_host_id_computer_use_hosts_id_fk", + "tableFrom": "chat_threads", + "tableTo": "computer_use_hosts", + "columnsFrom": [ + "computer_use_host_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.checkpoints": { + "name": "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 + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_compose_snapshot": { + "name": "agent_compose_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "artifact_snapshots": { + "name": "artifact_snapshots", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "volume_versions_snapshot": { + "name": "volume_versions_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "checkpoints_run_id_agent_runs_id_fk": { + "name": "checkpoints_run_id_agent_runs_id_fk", + "tableFrom": "checkpoints", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "checkpoints_conversation_id_conversations_id_fk": { + "name": "checkpoints_conversation_id_conversations_id_fk", + "tableFrom": "checkpoints", + "tableTo": "conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "checkpoints_run_id_unique": { + "name": "checkpoints_run_id_unique", + "nullsNotDistinct": false, + "columns": [ + "run_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cli_tokens": { + "name": "cli_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "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()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cli_tokens_token_unique": { + "name": "cli_tokens_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose_jobs": { + "name": "compose_jobs", + "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 + }, + "github_url": { + "name": "github_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "overwrite": { + "name": "overwrite", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "instructions": { + "name": "instructions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "sandbox_id": { + "name": "sandbox_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "result": { + "name": "result", + "type": "jsonb", + "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()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_compose_jobs_user_status": { + "name": "idx_compose_jobs_user_status", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_compose_jobs_created": { + "name": "idx_compose_jobs_created", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_compose_jobs_user_active": { + "name": "idx_compose_jobs_user_active", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "status IN ('pending', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.computer_use_authorization_requests": { + "name": "computer_use_authorization_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "request_token_hash": { + "name": "request_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_thread_id": { + "name": "chat_thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "slack_connection_id": { + "name": "slack_connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "slack_channel_id": { + "name": "slack_channel_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slack_thread_ts": { + "name": "slack_thread_ts", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "teams_connection_id": { + "name": "teams_connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "teams_conversation_id": { + "name": "teams_conversation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "teams_thread_id": { + "name": "teams_thread_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "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": { + "idx_computer_use_auth_requests_token_hash": { + "name": "idx_computer_use_auth_requests_token_hash", + "columns": [ + { + "expression": "request_token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_computer_use_auth_requests_org_user": { + "name": "idx_computer_use_auth_requests_org_user", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_computer_use_auth_requests_expires": { + "name": "idx_computer_use_auth_requests_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "computer_use_auth_requests_source_check": { + "name": "computer_use_auth_requests_source_check", + "value": "source IN ('chat', 'slack', 'teams')" + }, + "computer_use_auth_requests_scope_check": { + "name": "computer_use_auth_requests_scope_check", + "value": "(\n source = 'chat'\n AND chat_thread_id IS NOT NULL\n AND slack_connection_id IS NULL\n AND slack_channel_id IS NULL\n AND slack_thread_ts IS NULL\n AND teams_connection_id IS NULL\n AND teams_conversation_id IS NULL\n AND teams_thread_id IS NULL\n ) OR (\n source = 'slack'\n AND chat_thread_id IS NULL\n AND slack_connection_id IS NOT NULL\n AND slack_channel_id IS NOT NULL\n AND slack_thread_ts IS NOT NULL\n AND teams_connection_id IS NULL\n AND teams_conversation_id IS NULL\n AND teams_thread_id IS NULL\n ) OR (\n source = 'teams'\n AND chat_thread_id IS NULL\n AND slack_connection_id IS NULL\n AND slack_channel_id IS NULL\n AND slack_thread_ts IS NULL\n AND teams_connection_id IS NOT NULL\n AND teams_conversation_id IS NOT NULL\n AND teams_thread_id IS NOT NULL\n )" + } + }, + "isRLSEnabled": false + }, + "public.computer_use_command_audit_events": { + "name": "computer_use_command_audit_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "command_id": { + "name": "command_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "host_id": { + "name": "host_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "app": { + "name": "app", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approval_outcome": { + "name": "approval_outcome", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redacted_result": { + "name": "redacted_result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_computer_use_command_audit_command": { + "name": "idx_computer_use_command_audit_command", + "columns": [ + { + "expression": "command_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_computer_use_command_audit_org_user": { + "name": "idx_computer_use_command_audit_org_user", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_computer_use_command_audit_created": { + "name": "idx_computer_use_command_audit_created", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "computer_use_command_audit_events_command_id_computer_use_commands_id_fk": { + "name": "computer_use_command_audit_events_command_id_computer_use_commands_id_fk", + "tableFrom": "computer_use_command_audit_events", + "tableTo": "computer_use_commands", + "columnsFrom": [ + "command_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "computer_use_command_audit_events_host_id_computer_use_hosts_id_fk": { + "name": "computer_use_command_audit_events_host_id_computer_use_hosts_id_fk", + "tableFrom": "computer_use_command_audit_events", + "tableTo": "computer_use_hosts", + "columnsFrom": [ + "host_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.computer_use_commands": { + "name": "computer_use_commands", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "host_id": { + "name": "host_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "timeout_ms": { + "name": "timeout_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "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": { + "idx_computer_use_commands_host_status": { + "name": "idx_computer_use_commands_host_status", + "columns": [ + { + "expression": "host_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_computer_use_commands_org_user": { + "name": "idx_computer_use_commands_org_user", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_computer_use_commands_created": { + "name": "idx_computer_use_commands_created", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "computer_use_commands_host_id_computer_use_hosts_id_fk": { + "name": "computer_use_commands_host_id_computer_use_hosts_id_fk", + "tableFrom": "computer_use_commands", + "tableTo": "computer_use_hosts", + "columnsFrom": [ + "host_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.computer_use_hosts": { + "name": "computer_use_hosts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "installation_id": { + "name": "installation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "app_version": { + "name": "app_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "os_version": { + "name": "os_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "supported_capabilities": { + "name": "supported_capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "permissions": { + "name": "permissions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"accessibility\":false,\"screenRecording\":false}'::jsonb" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'online'" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_computer_use_hosts_token_hash": { + "name": "idx_computer_use_hosts_token_hash", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_computer_use_hosts_active_installation": { + "name": "idx_computer_use_hosts_active_installation", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "installation_id IS NOT NULL AND revoked_at IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_computer_use_hosts_org_user": { + "name": "idx_computer_use_hosts_org_user", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_computer_use_hosts_last_seen": { + "name": "idx_computer_use_hosts_last_seen", + "columns": [ + { + "expression": "last_seen_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.connector_external_code_sessions": { + "name": "connector_external_code_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_type": { + "name": "connector_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "auth_method": { + "name": "auth_method", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "connector_external_code_session_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "session_token_hash": { + "name": "session_token_hash", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "encrypted_provider_state": { + "name": "encrypted_provider_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authorization_url": { + "name": "authorization_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error_code": { + "name": "error_code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "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()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_connector_external_code_sessions_token": { + "name": "idx_connector_external_code_sessions_token", + "columns": [ + { + "expression": "session_token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_connector_external_code_sessions_owner_status": { + "name": "idx_connector_external_code_sessions_owner_status", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connector_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "auth_method", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_connector_external_code_sessions_expiration": { + "name": "idx_connector_external_code_sessions_expiration", + "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.connector_oauth_device_authorization_sessions": { + "name": "connector_oauth_device_authorization_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_type": { + "name": "connector_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "auth_method": { + "name": "auth_method", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "connector_oauth_device_authorization_session_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'awaiting_user_authorization'" + }, + "session_token_hash": { + "name": "session_token_hash", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "encrypted_provider_state": { + "name": "encrypted_provider_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_code": { + "name": "user_code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "verification_uri": { + "name": "verification_uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verification_uri_complete": { + "name": "verification_uri_complete", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "interval_seconds": { + "name": "interval_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "error_code": { + "name": "error_code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "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()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_connector_oauth_device_authorization_sessions_token": { + "name": "idx_connector_oauth_device_authorization_sessions_token", + "columns": [ + { + "expression": "session_token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_connector_oauth_device_authorization_sessions_owner_status": { + "name": "idx_connector_oauth_device_authorization_sessions_owner_status", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connector_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "auth_method", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_connector_oauth_device_authorization_sessions_expiration": { + "name": "idx_connector_oauth_device_authorization_sessions_expiration", + "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.connector_oauth_states": { + "name": "connector_oauth_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "auth_method": { + "name": "auth_method", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_context": { + "name": "oauth_context", + "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 + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_connector_oauth_states_state": { + "name": "idx_connector_oauth_states_state", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_connector_oauth_states_user_org": { + "name": "idx_connector_oauth_states_user_org", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_connector_oauth_states_expires_at": { + "name": "idx_connector_oauth_states_expires_at", + "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.connectors": { + "name": "connectors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "auth_method": { + "name": "auth_method", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "external_username": { + "name": "external_username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "external_email": { + "name": "external_email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "oauth_scopes": { + "name": "oauth_scopes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_expires_at": { + "name": "token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "needs_reconnect": { + "name": "needs_reconnect", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "reconnect_reason": { + "name": "reconnect_reason", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_connectors_org": { + "name": "idx_connectors_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_connectors_org_user_type": { + "name": "idx_connectors_org_user_type", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.credit_expires_record": { + "name": "credit_expires_record", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "stripe_invoice_id": { + "name": "stripe_invoice_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "remaining": { + "name": "remaining", + "type": "bigint", + "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()" + } + }, + "indexes": { + "idx_credit_expires_org_active": { + "name": "idx_credit_expires_org_active", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "remaining > 0", + "concurrently": false, + "method": "btree", + "with": {} + }, + "uq_credit_expires_invoice": { + "name": "uq_credit_expires_invoice", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stripe_invoice_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "stripe_invoice_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "uq_credit_expires_starter_grant": { + "name": "uq_credit_expires_starter_grant", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "source = 'starter_grant'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.desktop_auth_handoff_codes": { + "name": "desktop_auth_handoff_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "code_hash": { + "name": "code_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_desktop_auth_handoff_codes_expires": { + "name": "idx_desktop_auth_handoff_codes_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_desktop_auth_handoff_codes_user_created": { + "name": "idx_desktop_auth_handoff_codes_user_created", + "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": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "desktop_auth_handoff_codes_code_hash_unique": { + "name": "desktop_auth_handoff_codes_code_hash_unique", + "nullsNotDistinct": false, + "columns": [ + "code_hash" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.device_codes": { + "name": "device_codes", + "schema": "", + "columns": { + "code": { + "name": "code", + "type": "varchar(9)", + "primaryKey": true, + "notNull": true + }, + "purpose": { + "name": "purpose", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'cli'" + }, + "status": { + "name": "status", + "type": "device_code_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ble_session_nonce": { + "name": "ble_session_nonce", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "poll_token_hash": { + "name": "poll_token_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "poll_interval_seconds": { + "name": "poll_interval_seconds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cli_token_id": { + "name": "cli_token_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "chat_thread_id": { + "name": "chat_thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp", + "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 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.e2e_slack_mock_call_log": { + "name": "e2e_slack_mock_call_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "method": { + "name": "method", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "channel_id": { + "name": "channel_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_json": { + "name": "body_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_e2e_slack_mock_call_log_created_at": { + "name": "idx_e2e_slack_mock_call_log_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_e2e_slack_mock_call_log_method": { + "name": "idx_e2e_slack_mock_call_log_method", + "columns": [ + { + "expression": "method", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.e2e_telegram_mock_call_log": { + "name": "e2e_telegram_mock_call_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "method": { + "name": "method", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "bot_token": { + "name": "bot_token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "chat_id": { + "name": "chat_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_json": { + "name": "body_json", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_e2e_telegram_mock_call_log_created_at": { + "name": "idx_e2e_telegram_mock_call_log_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_e2e_telegram_mock_call_log_method": { + "name": "idx_e2e_telegram_mock_call_log_method", + "columns": [ + { + "expression": "method", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_e2e_telegram_mock_call_log_chat_id": { + "name": "idx_e2e_telegram_mock_call_log_chat_id", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_outbox": { + "name": "email_outbox", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "from_address": { + "name": "from_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "to_addresses": { + "name": "to_addresses", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "cc_addresses": { + "name": "cc_addresses", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "subject": { + "name": "subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reply_to": { + "name": "reply_to", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "headers": { + "name": "headers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "template": { + "name": "template", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "post_send_action": { + "name": "post_send_action", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "resend_id": { + "name": "resend_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "email_outbox_drain_idx": { + "name": "email_outbox_drain_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_retry_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_outbox_created_at_idx": { + "name": "email_outbox_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.email_reply_requests": { + "name": "email_reply_requests", + "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 + }, + "email_thread_session_id": { + "name": "email_thread_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "inbound_email_id": { + "name": "inbound_email_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "inbound_message_id": { + "name": "inbound_message_id", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_email_reply_requests_run": { + "name": "idx_email_reply_requests_run", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_reply_requests_run_id_agent_runs_id_fk": { + "name": "email_reply_requests_run_id_agent_runs_id_fk", + "tableFrom": "email_reply_requests", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_reply_requests_email_thread_session_id_email_thread_sessions_id_fk": { + "name": "email_reply_requests_email_thread_session_id_email_thread_sessions_id_fk", + "tableFrom": "email_reply_requests", + "tableTo": "email_thread_sessions", + "columnsFrom": [ + "email_thread_session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_suppressions": { + "name": "email_suppressions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email_address": { + "name": "email_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resend_email_id": { + "name": "resend_email_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "email_suppressions_email_lower_idx": { + "name": "email_suppressions_email_lower_idx", + "columns": [ + { + "expression": "lower(\"email_address\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_thread_sessions": { + "name": "email_thread_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_session_id": { + "name": "agent_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "last_email_message_id": { + "name": "last_email_message_id", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "reply_to_token": { + "name": "reply_to_token", + "type": "varchar(255)", + "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": { + "idx_email_thread_sessions_reply_token": { + "name": "idx_email_thread_sessions_reply_token", + "columns": [ + { + "expression": "reply_to_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_email_thread_sessions_user": { + "name": "idx_email_thread_sessions_user", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_thread_sessions_agent_id_agent_composes_id_fk": { + "name": "email_thread_sessions_agent_id_agent_composes_id_fk", + "tableFrom": "email_thread_sessions", + "tableTo": "agent_composes", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "email_thread_sessions_agent_session_id_agent_sessions_id_fk": { + "name": "email_thread_sessions_agent_session_id_agent_sessions_id_fk", + "tableFrom": "email_thread_sessions", + "tableTo": "agent_sessions", + "columnsFrom": [ + "agent_session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.export_jobs": { + "name": "export_jobs", + "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 + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "s3_key": { + "name": "s3_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "artifact_urls": { + "name": "artifact_urls", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_export_jobs_user_status": { + "name": "idx_export_jobs_user_status", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_export_jobs_created": { + "name": "idx_export_jobs_created", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_export_jobs_user_active": { + "name": "idx_export_jobs_user_active", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "status IN ('pending', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github_installations": { + "name": "github_installations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "installation_id": { + "name": "installation_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "encrypted_access_token": { + "name": "encrypted_access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "target_id": { + "name": "target_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "target_name": { + "name": "target_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "admin_github_user_id": { + "name": "admin_github_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "default_compose_id": { + "name": "default_compose_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "repo_configs": { + "name": "repo_configs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "github_installations_installation_id_unique": { + "name": "github_installations_installation_id_unique", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "installation_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_github_installations_org": { + "name": "idx_github_installations_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "github_installations_default_compose_id_agent_composes_id_fk": { + "name": "github_installations_default_compose_id_agent_composes_id_fk", + "tableFrom": "github_installations", + "tableTo": "agent_composes", + "columnsFrom": [ + "default_compose_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github_issue_sessions": { + "name": "github_issue_sessions", + "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 + }, + "installation_id": { + "name": "installation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "repo": { + "name": "repo", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "issue_number": { + "name": "issue_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "agent_session_id": { + "name": "agent_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "last_comment_id": { + "name": "last_comment_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_github_issue_sessions_installation_repo_issue": { + "name": "idx_github_issue_sessions_installation_repo_issue", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_github_issue_sessions_installation": { + "name": "idx_github_issue_sessions_installation", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "github_issue_sessions_installation_id_github_installations_id_fk": { + "name": "github_issue_sessions_installation_id_github_installations_id_fk", + "tableFrom": "github_issue_sessions", + "tableTo": "github_installations", + "columnsFrom": [ + "installation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "github_issue_sessions_agent_session_id_agent_sessions_id_fk": { + "name": "github_issue_sessions_agent_session_id_agent_sessions_id_fk", + "tableFrom": "github_issue_sessions", + "tableTo": "agent_sessions", + "columnsFrom": [ + "agent_session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github_user_links": { + "name": "github_user_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "github_user_id": { + "name": "github_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "installation_id": { + "name": "installation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "vm0_user_id": { + "name": "vm0_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_github_user_links_user_installation": { + "name": "idx_github_user_links_user_installation", + "columns": [ + { + "expression": "github_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "github_user_links_installation_id_github_installations_id_fk": { + "name": "github_user_links_installation_id_github_installations_id_fk", + "tableFrom": "github_user_links", + "tableTo": "github_installations", + "columnsFrom": [ + "installation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gmail_processed_events": { + "name": "gmail_processed_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "watch_state_id": { + "name": "watch_state_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trigger_id": { + "name": "trigger_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pubsub_message_id": { + "name": "pubsub_message_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "history_id": { + "name": "history_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "thread_id": { + "name": "thread_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_gmail_processed_events_event": { + "name": "idx_gmail_processed_events_event", + "columns": [ + { + "expression": "watch_state_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "history_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_gmail_processed_events_pubsub_message": { + "name": "idx_gmail_processed_events_pubsub_message", + "columns": [ + { + "expression": "pubsub_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "gmail_processed_events_watch_state_id_gmail_watch_states_id_fk": { + "name": "gmail_processed_events_watch_state_id_gmail_watch_states_id_fk", + "tableFrom": "gmail_processed_events", + "tableTo": "gmail_watch_states", + "columnsFrom": [ + "watch_state_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "gmail_processed_events_trigger_id_zero_workflow_triggers_id_fk": { + "name": "gmail_processed_events_trigger_id_zero_workflow_triggers_id_fk", + "tableFrom": "gmail_processed_events", + "tableTo": "zero_workflow_triggers", + "columnsFrom": [ + "trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gmail_watch_states": { + "name": "gmail_watch_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "email_address": { + "name": "email_address", + "type": "varchar(320)", + "primaryKey": false, + "notNull": true + }, + "topic_name": { + "name": "topic_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_history_id": { + "name": "last_history_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "watch_expiration_at": { + "name": "watch_expiration_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "last_watch_renewed_at": { + "name": "last_watch_renewed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "needs_rewatch": { + "name": "needs_rewatch", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_gmail_watch_states_connector_topic": { + "name": "idx_gmail_watch_states_connector_topic", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "topic_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_gmail_watch_states_email_topic": { + "name": "idx_gmail_watch_states_email_topic", + "columns": [ + { + "expression": "email_address", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "topic_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_gmail_watch_states_renewal": { + "name": "idx_gmail_watch_states_renewal", + "columns": [ + { + "expression": "watch_expiration_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "gmail_watch_states_connector_id_connectors_id_fk": { + "name": "gmail_watch_states_connector_id_connectors_id_fk", + "tableFrom": "gmail_watch_states", + "tableTo": "connectors", + "columnsFrom": [ + "connector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.google_calendar_event_snapshots": { + "name": "google_calendar_event_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "watch_state_id": { + "name": "watch_state_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "calendar_event_id": { + "name": "calendar_event_id", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": true + }, + "etag": { + "name": "etag", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start_at": { + "name": "start_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "end_at": { + "name": "end_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "event_created_at": { + "name": "event_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "event_updated_at": { + "name": "event_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "snapshot": { + "name": "snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_google_calendar_event_snapshots_event": { + "name": "idx_google_calendar_event_snapshots_event", + "columns": [ + { + "expression": "watch_state_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "calendar_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_google_calendar_event_snapshots_updated": { + "name": "idx_google_calendar_event_snapshots_updated", + "columns": [ + { + "expression": "event_updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "google_calendar_event_snapshots_watch_state_id_google_calendar_watch_states_id_fk": { + "name": "google_calendar_event_snapshots_watch_state_id_google_calendar_watch_states_id_fk", + "tableFrom": "google_calendar_event_snapshots", + "tableTo": "google_calendar_watch_states", + "columnsFrom": [ + "watch_state_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.google_calendar_processed_events": { + "name": "google_calendar_processed_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "watch_state_id": { + "name": "watch_state_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trigger_id": { + "name": "trigger_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "channel_id": { + "name": "channel_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "resource_state": { + "name": "resource_state", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "calendar_event_id": { + "name": "calendar_event_id", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": true + }, + "event_change_key": { + "name": "event_change_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'created'" + }, + "event_created_at": { + "name": "event_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "event_updated_at": { + "name": "event_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_google_calendar_processed_events_event": { + "name": "idx_google_calendar_processed_events_event", + "columns": [ + { + "expression": "watch_state_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "calendar_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_change_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_google_calendar_processed_events_channel": { + "name": "idx_google_calendar_processed_events_channel", + "columns": [ + { + "expression": "channel_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "google_calendar_processed_events_watch_state_id_google_calendar_watch_states_id_fk": { + "name": "google_calendar_processed_events_watch_state_id_google_calendar_watch_states_id_fk", + "tableFrom": "google_calendar_processed_events", + "tableTo": "google_calendar_watch_states", + "columnsFrom": [ + "watch_state_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "google_calendar_processed_events_trigger_id_zero_workflow_triggers_id_fk": { + "name": "google_calendar_processed_events_trigger_id_zero_workflow_triggers_id_fk", + "tableFrom": "google_calendar_processed_events", + "tableTo": "zero_workflow_triggers", + "columnsFrom": [ + "trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.google_calendar_watch_states": { + "name": "google_calendar_watch_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "calendar_id": { + "name": "calendar_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel_id": { + "name": "channel_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "channel_token": { + "name": "channel_token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "resource_uri": { + "name": "resource_uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sync_token": { + "name": "sync_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "watch_expiration_at": { + "name": "watch_expiration_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "last_watch_renewed_at": { + "name": "last_watch_renewed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "needs_rewatch": { + "name": "needs_rewatch", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_google_calendar_watch_states_connector_calendar": { + "name": "idx_google_calendar_watch_states_connector_calendar", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "calendar_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_google_calendar_watch_states_channel": { + "name": "idx_google_calendar_watch_states_channel", + "columns": [ + { + "expression": "channel_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_google_calendar_watch_states_resource": { + "name": "idx_google_calendar_watch_states_resource", + "columns": [ + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_google_calendar_watch_states_renewal": { + "name": "idx_google_calendar_watch_states_renewal", + "columns": [ + { + "expression": "watch_expiration_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "google_calendar_watch_states_connector_id_connectors_id_fk": { + "name": "google_calendar_watch_states_connector_id_connectors_id_fk", + "tableFrom": "google_calendar_watch_states", + "tableTo": "connectors", + "columnsFrom": [ + "connector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.google_workspace_event_subscription_states": { + "name": "google_workspace_event_subscription_states", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "target_resource": { + "name": "target_resource", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_types": { + "name": "event_types", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "event_types_key": { + "name": "event_types_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subscription_name": { + "name": "subscription_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "pubsub_topic": { + "name": "pubsub_topic", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "expire_time": { + "name": "expire_time", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "last_renewed_at": { + "name": "last_renewed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "needs_repair": { + "name": "needs_repair", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_google_workspace_event_subscription_scope": { + "name": "idx_google_workspace_event_subscription_scope", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_resource", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pubsub_topic", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_types_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_google_workspace_event_subscription_name": { + "name": "idx_google_workspace_event_subscription_name", + "columns": [ + { + "expression": "subscription_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_google_workspace_event_subscription_owner": { + "name": "idx_google_workspace_event_subscription_owner", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_google_workspace_event_subscription_renewal": { + "name": "idx_google_workspace_event_subscription_renewal", + "columns": [ + { + "expression": "expire_time", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "google_workspace_event_subscription_states_connector_id_connectors_id_fk": { + "name": "google_workspace_event_subscription_states_connector_id_connectors_id_fk", + "tableFrom": "google_workspace_event_subscription_states", + "tableTo": "connectors", + "columnsFrom": [ + "connector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.google_workspace_processed_events": { + "name": "google_workspace_processed_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subscription_state_id": { + "name": "subscription_state_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trigger_id": { + "name": "trigger_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pubsub_message_id": { + "name": "pubsub_message_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "cloud_event_id": { + "name": "cloud_event_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "cloud_event_type": { + "name": "cloud_event_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "conference_record_name": { + "name": "conference_record_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "transcript_name": { + "name": "transcript_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_google_workspace_processed_events_cloudevent": { + "name": "idx_google_workspace_processed_events_cloudevent", + "columns": [ + { + "expression": "subscription_state_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cloud_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_google_workspace_processed_events_transcript": { + "name": "idx_google_workspace_processed_events_transcript", + "columns": [ + { + "expression": "subscription_state_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "transcript_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_google_workspace_processed_events_pubsub_message": { + "name": "idx_google_workspace_processed_events_pubsub_message", + "columns": [ + { + "expression": "pubsub_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "google_workspace_processed_events_subscription_state_id_google_workspace_event_subscription_states_id_fk": { + "name": "google_workspace_processed_events_subscription_state_id_google_workspace_event_subscription_states_id_fk", + "tableFrom": "google_workspace_processed_events", + "tableTo": "google_workspace_event_subscription_states", + "columnsFrom": [ + "subscription_state_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "google_workspace_processed_events_trigger_id_zero_workflow_triggers_id_fk": { + "name": "google_workspace_processed_events_trigger_id_zero_workflow_triggers_id_fk", + "tableFrom": "google_workspace_processed_events", + "tableTo": "zero_workflow_triggers", + "columnsFrom": [ + "trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hosted_deployments": { + "name": "hosted_deployments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "site_id": { + "name": "site_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'uploading'" + }, + "r2_prefix": { + "name": "r2_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "manifest": { + "name": "manifest", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "manifest_hash": { + "name": "manifest_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "entrypoint": { + "name": "entrypoint", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'/index.html'" + }, + "spa_fallback": { + "name": "spa_fallback", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "file_count": { + "name": "file_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ready_at": { + "name": "ready_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_hosted_deployments_site": { + "name": "idx_hosted_deployments_site", + "columns": [ + { + "expression": "site_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_hosted_deployments_org": { + "name": "idx_hosted_deployments_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_hosted_deployments_status": { + "name": "idx_hosted_deployments_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hosted_deployments_site_id_hosted_sites_id_fk": { + "name": "hosted_deployments_site_id_hosted_sites_id_fk", + "tableFrom": "hosted_deployments", + "tableTo": "hosted_sites", + "columnsFrom": [ + "site_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hosted_sites": { + "name": "hosted_sites", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "public_slug": { + "name": "public_slug", + "type": "varchar(96)", + "primaryKey": false, + "notNull": true + }, + "active_deployment_id": { + "name": "active_deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_from_run_id": { + "name": "created_from_run_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()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_hosted_sites_org": { + "name": "idx_hosted_sites_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_hosted_sites_org_slug": { + "name": "idx_hosted_sites_org_slug", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_hosted_sites_public_slug": { + "name": "idx_hosted_sites_public_slug", + "columns": [ + { + "expression": "public_slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.html_artifact_edit_drafts": { + "name": "html_artifact_edit_drafts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_thread_id": { + "name": "chat_thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "artifact_url": { + "name": "artifact_url", + "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": { + "idx_html_artifact_edit_drafts_thread_artifact": { + "name": "idx_html_artifact_edit_drafts_thread_artifact", + "columns": [ + { + "expression": "chat_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "artifact_url", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_html_artifact_edit_drafts_thread": { + "name": "idx_html_artifact_edit_drafts_thread", + "columns": [ + { + "expression": "chat_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "html_artifact_edit_drafts_chat_thread_id_chat_threads_id_fk": { + "name": "html_artifact_edit_drafts_chat_thread_id_chat_threads_id_fk", + "tableFrom": "html_artifact_edit_drafts", + "tableTo": "chat_threads", + "columnsFrom": [ + "chat_thread_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.insights_daily": { + "name": "insights_daily", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "uq_insights_daily_org_user_date": { + "name": "uq_insights_daily_org_user_date", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_insights_daily_org_user_date_desc": { + "name": "idx_insights_daily_org_user_date_desc", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_change_items": { + "name": "memory_change_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "summary_id": { + "name": "summary_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "diff": { + "name": "diff", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_memory_change_items_summary": { + "name": "idx_memory_change_items_summary", + "columns": [ + { + "expression": "summary_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_change_items_summary_id_memory_change_summaries_id_fk": { + "name": "memory_change_items_summary_id_memory_change_summaries_id_fk", + "tableFrom": "memory_change_items", + "tableTo": "memory_change_summaries", + "columnsFrom": [ + "summary_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_change_summaries": { + "name": "memory_change_summaries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "from_version_id": { + "name": "from_version_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "to_version_id": { + "name": "to_version_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "uq_memory_change_summaries_org_user_date": { + "name": "uq_memory_change_summaries_org_user_date", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memory_change_summaries_org_user_date_desc": { + "name": "idx_memory_change_summaries_org_user_date_desc", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memories": { + "name": "memories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 80 + }, + "source_count": { + "name": "source_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_memories_scope_kind": { + "name": "idx_memories_scope_kind", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memories_entity_status": { + "name": "idx_memories_entity_status", + "columns": [ + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memories_entity_id_memory_entities_id_fk": { + "name": "memories_entity_id_memory_entities_id_fk", + "tableFrom": "memories", + "tableTo": "memory_entities", + "columnsFrom": [ + "entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_edges": { + "name": "memory_edges", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_memory_id": { + "name": "from_memory_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "to_memory_id": { + "name": "to_memory_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "edge_type": { + "name": "edge_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_memory_edges_unique": { + "name": "idx_memory_edges_unique", + "columns": [ + { + "expression": "from_memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "to_memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "edge_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memory_edges_to": { + "name": "idx_memory_edges_to", + "columns": [ + { + "expression": "to_memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_edges_from_memory_id_memories_id_fk": { + "name": "memory_edges_from_memory_id_memories_id_fk", + "tableFrom": "memory_edges", + "tableTo": "memories", + "columnsFrom": [ + "from_memory_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memory_edges_to_memory_id_memories_id_fk": { + "name": "memory_edges_to_memory_id_memories_id_fk", + "tableFrom": "memory_edges", + "tableTo": "memories", + "columnsFrom": [ + "to_memory_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_entities": { + "name": "memory_entities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "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": { + "idx_memory_entities_scope_type": { + "name": "idx_memory_entities_scope_type", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_entity_aliases": { + "name": "memory_entity_aliases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "alias_type": { + "name": "alias_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "alias_value": { + "name": "alias_value", + "type": "varchar(512)", + "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": { + "idx_memory_entity_aliases_alias": { + "name": "idx_memory_entity_aliases_alias", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "alias_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "alias_value", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memory_entity_aliases_entity": { + "name": "idx_memory_entity_aliases_entity", + "columns": [ + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_entity_aliases_entity_id_memory_entities_id_fk": { + "name": "memory_entity_aliases_entity_id_memory_entities_id_fk", + "tableFrom": "memory_entity_aliases", + "tableTo": "memory_entities", + "columnsFrom": [ + "entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_profiles": { + "name": "memory_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "section": { + "name": "section", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_memory_count": { + "name": "source_memory_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_memory_profiles_entity_section": { + "name": "idx_memory_profiles_entity_section", + "columns": [ + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "section", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memory_profiles_scope": { + "name": "idx_memory_profiles_scope", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_profiles_entity_id_memory_entities_id_fk": { + "name": "memory_profiles_entity_id_memory_entities_id_fk", + "tableFrom": "memory_profiles", + "tableTo": "memory_entities", + "columnsFrom": [ + "entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_search_entries": { + "name": "memory_search_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "memory_id": { + "name": "memory_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "entry_kind": { + "name": "entry_kind", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "memory_kind": { + "name": "memory_kind", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": true + }, + "embedding_model": { + "name": "embedding_model", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 80 + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_memory_search_entries_memory_kind": { + "name": "idx_memory_search_entries_memory_kind", + "columns": [ + { + "expression": "memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entry_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "embedding_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memory_search_entries_scope_status_kind": { + "name": "idx_memory_search_entries_scope_status_kind", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "memory_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memory_search_entries_entity": { + "name": "idx_memory_search_entries_entity", + "columns": [ + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memory_search_entries_embedding_hnsw": { + "name": "idx_memory_search_entries_embedding_hnsw", + "columns": [ + { + "expression": "embedding vector_cosine_ops", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": { + "memory_search_entries_memory_id_memories_id_fk": { + "name": "memory_search_entries_memory_id_memories_id_fk", + "tableFrom": "memory_search_entries", + "tableTo": "memories", + "columnsFrom": [ + "memory_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memory_search_entries_entity_id_memory_entities_id_fk": { + "name": "memory_search_entries_entity_id_memory_entities_id_fk", + "tableFrom": "memory_search_entries", + "tableTo": "memory_entities", + "columnsFrom": [ + "entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_source_links": { + "name": "memory_source_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "memory_id": { + "name": "memory_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_memory_source_links_pair": { + "name": "idx_memory_source_links_pair", + "columns": [ + { + "expression": "memory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memory_source_links_source": { + "name": "idx_memory_source_links_source", + "columns": [ + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_source_links_memory_id_memories_id_fk": { + "name": "memory_source_links_memory_id_memories_id_fk", + "tableFrom": "memory_source_links", + "tableTo": "memories", + "columnsFrom": [ + "memory_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memory_source_links_source_id_memory_sources_id_fk": { + "name": "memory_source_links_source_id_memory_sources_id_fk", + "tableFrom": "memory_source_links", + "tableTo": "memory_sources", + "columnsFrom": [ + "source_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_sources": { + "name": "memory_sources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "source_type": { + "name": "source_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_hash": { + "name": "content_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_memory_sources_external": { + "name": "idx_memory_sources_external", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memory_sources_scope_provider": { + "name": "idx_memory_sources_scope_provider", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_memory_sources_occurred": { + "name": "idx_memory_sources_occurred", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_provider_auth_sessions": { + "name": "model_provider_auth_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "connector_type": { + "name": "connector_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "model_provider_auth_session_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'initializing'" + }, + "sandbox_id": { + "name": "sandbox_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "approval_url": { + "name": "approval_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verification_code": { + "name": "verification_code", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "encrypted_provider_state": { + "name": "encrypted_provider_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "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()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_model_provider_auth_sessions_owner_status": { + "name": "idx_model_provider_auth_sessions_owner_status", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connector_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_provider_auth_sessions_expiration": { + "name": "idx_model_provider_auth_sessions_expiration", + "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": {} + }, + "idx_model_provider_auth_sessions_sandbox": { + "name": "idx_model_provider_auth_sessions_sandbox", + "columns": [ + { + "expression": "sandbox_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"model_provider_auth_sessions\".\"sandbox_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_providers": { + "name": "model_providers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "secret_id": { + "name": "secret_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "auth_method": { + "name": "auth_method", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "selected_model": { + "name": "selected_model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_expires_at": { + "name": "token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "needs_reconnect": { + "name": "needs_reconnect", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_refresh_error_code": { + "name": "last_refresh_error_code", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "workspace_name": { + "name": "workspace_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "plan_type": { + "name": "plan_type", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false + }, + "subscription_reset_period": { + "name": "subscription_reset_period", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "subscription_next_reset_at": { + "name": "subscription_next_reset_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_model_providers_secret": { + "name": "idx_model_providers_secret", + "columns": [ + { + "expression": "secret_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_providers_org": { + "name": "idx_model_providers_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_providers_org_user_type": { + "name": "idx_model_providers_org_user_type", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_providers_one_default_per_user": { + "name": "idx_model_providers_one_default_per_user", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "is_default = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_providers_secret_id_secrets_id_fk": { + "name": "model_providers_secret_id_secrets_id_fk", + "tableFrom": "model_providers", + "tableTo": "secrets", + "columnsFrom": [ + "secret_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_stat": { + "name": "model_stat", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "hour_start": { + "name": "hour_start", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "model_provider": { + "name": "model_provider", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "request_count": { + "name": "request_count", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "org_count": { + "name": "org_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "user_count": { + "name": "user_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "input_tokens": { + "name": "input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "output_tokens": { + "name": "output_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cache_read_input_tokens": { + "name": "cache_read_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cache_creation_input_tokens": { + "name": "cache_creation_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_tokens": { + "name": "total_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "credits_charged": { + "name": "credits_charged", + "type": "bigint", + "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": { + "uq_model_stat_hour_model_provider": { + "name": "uq_model_stat_hour_model_provider", + "columns": [ + { + "expression": "hour_start", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "model", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "model_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_stat_hour_start": { + "name": "idx_model_stat_hour_start", + "columns": [ + { + "expression": "hour_start", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_stat_model_hour": { + "name": "idx_model_stat_model_hour", + "columns": [ + { + "expression": "model", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "hour_start", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_usage_observation": { + "name": "model_usage_observation", + "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": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "model_provider_type": { + "name": "model_provider_type", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "category": { + "name": "category", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "observed_at": { + "name": "observed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "uq_model_usage_observation_idempotency_key": { + "name": "uq_model_usage_observation_idempotency_key", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_usage_observation_run_id": { + "name": "idx_model_usage_observation_run_id", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_usage_observation_observed_at": { + "name": "idx_model_usage_observation_observed_at", + "columns": [ + { + "expression": "observed_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_usage_observation_model_observed_at": { + "name": "idx_model_usage_observation_model_observed_at", + "columns": [ + { + "expression": "model", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "observed_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_model_usage_observation_org_observed_at": { + "name": "idx_model_usage_observation_org_observed_at", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "observed_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_usage_observation_run_id_agent_runs_id_fk": { + "name": "model_usage_observation_run_id_agent_runs_id_fk", + "tableFrom": "model_usage_observation", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notion_webhook_events": { + "name": "notion_webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "notion_event_id": { + "name": "notion_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "page_id": { + "name": "page_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_notion_webhook_events_event_id": { + "name": "idx_notion_webhook_events_event_id", + "columns": [ + { + "expression": "notion_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_notion_webhook_events_page": { + "name": "idx_notion_webhook_events_page", + "columns": [ + { + "expression": "page_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notion_webhook_secrets": { + "name": "notion_webhook_secrets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "encrypted_verification_token": { + "name": "encrypted_verification_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": 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": { + "idx_notion_webhook_secrets_active": { + "name": "idx_notion_webhook_secrets_active", + "columns": [ + { + "expression": "active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_notion_webhook_secrets_active_single": { + "name": "idx_notion_webhook_secrets_active_single", + "columns": [ + { + "expression": "active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "active = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notion_workflow_pending_events": { + "name": "notion_workflow_pending_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "trigger_id": { + "name": "trigger_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_id": { + "name": "page_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_type": { + "name": "scope_type", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_family": { + "name": "event_family", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "default": "'new_child_page'" + }, + "status": { + "name": "status", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "first_notion_event_id": { + "name": "first_notion_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "latest_notion_event_id": { + "name": "latest_notion_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "first_event_at": { + "name": "first_event_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "latest_event_at": { + "name": "latest_event_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "latest_event_context": { + "name": "latest_event_context", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "run_after": { + "name": "run_after", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "page_title": { + "name": "page_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "page_url": { + "name": "page_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_title": { + "name": "parent_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_url": { + "name": "parent_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "skip_reason": { + "name": "skip_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_notion_pending_events_trigger_page_family_active": { + "name": "idx_notion_pending_events_trigger_page_family_active", + "columns": [ + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "page_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_family", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "status IN ('pending', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_notion_pending_events_due": { + "name": "idx_notion_pending_events_due", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_notion_pending_events_page_pending": { + "name": "idx_notion_pending_events_page_pending", + "columns": [ + { + "expression": "page_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_notion_pending_events_scope": { + "name": "idx_notion_pending_events_scope", + "columns": [ + { + "expression": "scope_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notion_workflow_pending_events_trigger_id_zero_workflow_triggers_id_fk": { + "name": "notion_workflow_pending_events_trigger_id_zero_workflow_triggers_id_fk", + "tableFrom": "notion_workflow_pending_events", + "tableTo": "zero_workflow_triggers", + "columnsFrom": [ + "trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_cache": { + "name": "org_cache", + "schema": "", + "columns": { + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cached_at": { + "name": "cached_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_org_cache_slug": { + "name": "idx_org_cache_slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_concurrency_entitlements": { + "name": "org_concurrency_entitlements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_invoice_id": { + "name": "stripe_invoice_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_invoice_line_id": { + "name": "stripe_invoice_line_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_price_id": { + "name": "stripe_price_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slots": { + "name": "slots", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp", + "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()" + } + }, + "indexes": { + "uq_org_concurrency_entitlements_invoice_line": { + "name": "uq_org_concurrency_entitlements_invoice_line", + "columns": [ + { + "expression": "stripe_invoice_line_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_concurrency_entitlements_org_active": { + "name": "idx_org_concurrency_entitlements_org_active", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "starts_at", + "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": { + "chk_org_concurrency_entitlements_slots": { + "name": "chk_org_concurrency_entitlements_slots", + "value": "\"org_concurrency_entitlements\".\"slots\" > 0" + }, + "chk_org_concurrency_entitlements_window": { + "name": "chk_org_concurrency_entitlements_window", + "value": "\"org_concurrency_entitlements\".\"expires_at\" > \"org_concurrency_entitlements\".\"starts_at\"" + } + }, + "isRLSEnabled": false + }, + "public.org_concurrency_subscriptions": { + "name": "org_concurrency_subscriptions", + "schema": "", + "columns": { + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_price_id": { + "name": "stripe_price_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slots": { + "name": "slots", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "subscription_status": { + "name": "subscription_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "current_period_end": { + "name": "current_period_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_org_concurrency_subscriptions_org": { + "name": "idx_org_concurrency_subscriptions_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_concurrency_subscriptions_status_period": { + "name": "idx_org_concurrency_subscriptions_status_period", + "columns": [ + { + "expression": "subscription_status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "current_period_end", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chk_org_concurrency_subscriptions_slots": { + "name": "chk_org_concurrency_subscriptions_slots", + "value": "\"org_concurrency_subscriptions\".\"slots\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.org_custom_connector_secrets": { + "name": "org_custom_connector_secrets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "connector_id": { + "name": "connector_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_value": { + "name": "encrypted_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": { + "idx_org_custom_connector_secrets_connector": { + "name": "idx_org_custom_connector_secrets_connector", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_custom_connector_secrets_user": { + "name": "idx_org_custom_connector_secrets_user", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_custom_connector_secrets_connector_user": { + "name": "idx_org_custom_connector_secrets_connector_user", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_custom_connector_values": { + "name": "org_custom_connector_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "connector_id": { + "name": "connector_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "encrypted_value": { + "name": "encrypted_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": { + "idx_org_custom_connector_values_connector": { + "name": "idx_org_custom_connector_values_connector", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_custom_connector_values_user": { + "name": "idx_org_custom_connector_values_user", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_custom_connector_values_unique": { + "name": "idx_org_custom_connector_values_unique", + "columns": [ + { + "expression": "connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "org_custom_connector_values_connector_id_org_custom_connectors_id_fk": { + "name": "org_custom_connector_values_connector_id_org_custom_connectors_id_fk", + "tableFrom": "org_custom_connector_values", + "tableTo": "org_custom_connectors", + "columnsFrom": [ + "connector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_custom_connectors": { + "name": "org_custom_connectors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "prefixes": { + "name": "prefixes", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "header_name": { + "name": "header_name", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "header_template": { + "name": "header_template", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix_templates": { + "name": "prefix_templates", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "fields": { + "name": "fields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "header_injections": { + "name": "header_injections", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "query_injections": { + "name": "query_injections", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "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": { + "idx_org_custom_connectors_org": { + "name": "idx_org_custom_connectors_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_custom_connectors_org_slug": { + "name": "idx_org_custom_connectors_org_slug", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_members_cache": { + "name": "org_members_cache", + "schema": "", + "columns": { + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "cached_at": { + "name": "cached_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "org_members_cache_org_id_user_id_pk": { + "name": "org_members_cache_org_id_user_id_pk", + "columns": [ + "org_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_members_metadata": { + "name": "org_members_metadata", + "schema": "", + "columns": { + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "onboarding_role": { + "name": "onboarding_role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pinned_agent_ids": { + "name": "pinned_agent_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'[]'::jsonb" + }, + "send_mode": { + "name": "send_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'enter'" + }, + "selected_model": { + "name": "selected_model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "onboarding_done": { + "name": "onboarding_done", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "capture_network_bodies_remaining": { + "name": "capture_network_bodies_remaining", + "type": "integer", + "primaryKey": false, + "notNull": false, + "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": { + "org_members_metadata_org_id_user_id_pk": { + "name": "org_members_metadata_org_id_user_id_pk", + "columns": [ + "org_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_metadata": { + "name": "org_metadata", + "schema": "", + "columns": { + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "credits": { + "name": "credits", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tier": { + "name": "tier", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'limited-free-1'" + }, + "default_agent_id": { + "name": "default_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "onboarding_payment_pending": { + "name": "onboarding_payment_pending", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "onboarding_complete": { + "name": "onboarding_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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 + }, + "subscription_status": { + "name": "subscription_status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "current_period_end": { + "name": "current_period_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pending_subscription_schedule_id": { + "name": "pending_subscription_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pending_subscription_target_tier": { + "name": "pending_subscription_target_tier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pending_subscription_change_at": { + "name": "pending_subscription_change_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_processed_invoice_id": { + "name": "last_processed_invoice_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_recharge_enabled": { + "name": "auto_recharge_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auto_recharge_threshold": { + "name": "auto_recharge_threshold", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "auto_recharge_amount": { + "name": "auto_recharge_amount", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "auto_recharge_pending_at": { + "name": "auto_recharge_pending_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "uq_org_stripe_customer": { + "name": "uq_org_stripe_customer", + "columns": [ + { + "expression": "stripe_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "org_metadata_default_agent_id_agent_composes_id_fk": { + "name": "org_metadata_default_agent_id_agent_composes_id_fk", + "tableFrom": "org_metadata", + "tableTo": "agent_composes", + "columnsFrom": [ + "default_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_model_policies": { + "name": "org_model_policies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "default_provider_type": { + "name": "default_provider_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "default": "'vm0'" + }, + "credential_scope": { + "name": "credential_scope", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'org'" + }, + "model_provider_id": { + "name": "model_provider_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by_user_id": { + "name": "updated_by_user_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": { + "idx_org_model_policies_org_model": { + "name": "idx_org_model_policies_org_model", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_model_policies_one_default_per_org": { + "name": "idx_org_model_policies_one_default_per_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "is_default = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_model_policies_provider": { + "name": "idx_org_model_policies_provider", + "columns": [ + { + "expression": "model_provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "model_provider_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "org_model_policies_model_provider_id_model_providers_id_fk": { + "name": "org_model_policies_model_provider_id_model_providers_id_fk", + "tableFrom": "org_model_policies", + "tableTo": "model_providers", + "columnsFrom": [ + "model_provider_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chk_org_model_policies_credential_scope": { + "name": "chk_org_model_policies_credential_scope", + "value": "credential_scope IN ('org', 'member')" + }, + "chk_org_model_policies_member_scope_no_provider_id": { + "name": "chk_org_model_policies_member_scope_no_provider_id", + "value": "credential_scope <> 'member' OR model_provider_id IS NULL" + }, + "chk_org_model_policies_builtin_route_no_provider_id": { + "name": "chk_org_model_policies_builtin_route_no_provider_id", + "value": "default_provider_type <> 'vm0' OR model_provider_id IS NULL" + }, + "chk_org_model_policies_member_scope_oauth_provider": { + "name": "chk_org_model_policies_member_scope_oauth_provider", + "value": "credential_scope <> 'member' OR default_provider_type IN ('claude-code-oauth-token', 'codex-oauth-token')" + }, + "chk_org_model_policies_oauth_provider_member_scope": { + "name": "chk_org_model_policies_oauth_provider_member_scope", + "value": "default_provider_type NOT IN ('claude-code-oauth-token', 'codex-oauth-token') OR credential_scope = 'member'" + } + }, + "isRLSEnabled": false + }, + "public.org_plan_entitlements": { + "name": "org_plan_entitlements", + "schema": "", + "columns": { + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "plan_key": { + "name": "plan_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan_rank": { + "name": "plan_rank", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "base_concurrency_limit": { + "name": "base_concurrency_limit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "can_buy_concurrency": { + "name": "can_buy_concurrency", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "auto_recharge_allowed": { + "name": "auto_recharge_allowed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "support_byok": { + "name": "support_byok", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "restricted_vm0_models": { + "name": "restricted_vm0_models", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "video_generation_allowed": { + "name": "video_generation_allowed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "audio_lifetime_limit": { + "name": "audio_lifetime_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "audio_daily_rate_limit": { + "name": "audio_daily_rate_limit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "audio_daily_duration_seconds": { + "name": "audio_daily_duration_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_product_id": { + "name": "stripe_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_price_id": { + "name": "stripe_price_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "current_period_start": { + "name": "current_period_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "current_period_end": { + "name": "current_period_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancel_at": { + "name": "cancel_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "metadata_version": { + "name": "metadata_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'1'" + }, + "metadata_hash": { + "name": "metadata_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_metadata": { + "name": "source_metadata", + "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": { + "uq_org_plan_entitlements_stripe_subscription": { + "name": "uq_org_plan_entitlements_stripe_subscription", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_plan_entitlements_status": { + "name": "idx_org_plan_entitlements_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_plan_entitlements_source": { + "name": "idx_org_plan_entitlements_source", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_plan_entitlements_expires": { + "name": "idx_org_plan_entitlements_expires", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chk_org_plan_entitlements_plan_rank": { + "name": "chk_org_plan_entitlements_plan_rank", + "value": "\"org_plan_entitlements\".\"plan_rank\" >= 0" + }, + "chk_org_plan_entitlements_base_concurrency": { + "name": "chk_org_plan_entitlements_base_concurrency", + "value": "\"org_plan_entitlements\".\"base_concurrency_limit\" >= 0" + }, + "chk_org_plan_entitlements_audio_lifetime": { + "name": "chk_org_plan_entitlements_audio_lifetime", + "value": "\"org_plan_entitlements\".\"audio_lifetime_limit\" IS NULL OR \"org_plan_entitlements\".\"audio_lifetime_limit\" >= 0" + }, + "chk_org_plan_entitlements_audio_daily_rate": { + "name": "chk_org_plan_entitlements_audio_daily_rate", + "value": "\"org_plan_entitlements\".\"audio_daily_rate_limit\" >= 0" + }, + "chk_org_plan_entitlements_audio_daily_duration": { + "name": "chk_org_plan_entitlements_audio_daily_duration", + "value": "\"org_plan_entitlements\".\"audio_daily_duration_seconds\" >= 0" + }, + "chk_org_plan_entitlements_period": { + "name": "chk_org_plan_entitlements_period", + "value": "\"org_plan_entitlements\".\"current_period_start\" IS NULL OR \"org_plan_entitlements\".\"current_period_end\" IS NULL OR \"org_plan_entitlements\".\"current_period_end\" > \"org_plan_entitlements\".\"current_period_start\"" + } + }, + "isRLSEnabled": false + }, + "public.org_promo_redemption": { + "name": "org_promo_redemption", + "schema": "", + "columns": { + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "campaign_key": { + "name": "campaign_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_session_id": { + "name": "stripe_session_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": { + "uq_org_promo_redemption": { + "name": "uq_org_promo_redemption", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "campaign_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_usage_allowance_entitlements": { + "name": "org_usage_allowance_entitlements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "status": { + "name": "status", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "short_window_seconds": { + "name": "short_window_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "short_window_units": { + "name": "short_window_units", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "weekly_window_seconds": { + "name": "weekly_window_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 604800 + }, + "weekly_window_units": { + "name": "weekly_window_units", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "effective_at": { + "name": "effective_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "stripe_invoice_id": { + "name": "stripe_invoice_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": { + "uq_org_usage_allowance_entitlements_org": { + "name": "uq_org_usage_allowance_entitlements_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_usage_allowance_entitlements_status": { + "name": "idx_org_usage_allowance_entitlements_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_usage_allowance_entitlements_stripe_subscription": { + "name": "idx_org_usage_allowance_entitlements_stripe_subscription", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chk_org_usage_allowance_short_window_seconds": { + "name": "chk_org_usage_allowance_short_window_seconds", + "value": "\"org_usage_allowance_entitlements\".\"short_window_seconds\" > 0" + }, + "chk_org_usage_allowance_short_window_units": { + "name": "chk_org_usage_allowance_short_window_units", + "value": "\"org_usage_allowance_entitlements\".\"short_window_units\" > 0" + }, + "chk_org_usage_allowance_weekly_window_seconds": { + "name": "chk_org_usage_allowance_weekly_window_seconds", + "value": "\"org_usage_allowance_entitlements\".\"weekly_window_seconds\" > 0" + }, + "chk_org_usage_allowance_weekly_window_units": { + "name": "chk_org_usage_allowance_weekly_window_units", + "value": "\"org_usage_allowance_entitlements\".\"weekly_window_units\" > 0" + }, + "chk_org_usage_allowance_entitlement_time": { + "name": "chk_org_usage_allowance_entitlement_time", + "value": "\"org_usage_allowance_entitlements\".\"expires_at\" IS NULL OR \"org_usage_allowance_entitlements\".\"expires_at\" > \"org_usage_allowance_entitlements\".\"effective_at\"" + } + }, + "isRLSEnabled": false + }, + "public.org_usage_allowance_windows": { + "name": "org_usage_allowance_windows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entitlement_id": { + "name": "entitlement_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "unit_limit": { + "name": "unit_limit", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "consumed_units": { + "name": "consumed_units", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_by_run_id": { + "name": "created_by_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_org_usage_allowance_windows_org_kind_starts": { + "name": "idx_org_usage_allowance_windows_org_kind_starts", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "starts_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_org_usage_allowance_windows_org_kind_expires": { + "name": "idx_org_usage_allowance_windows_org_kind_expires", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "org_usage_allowance_windows_entitlement_id_org_usage_allowance_entitlements_id_fk": { + "name": "org_usage_allowance_windows_entitlement_id_org_usage_allowance_entitlements_id_fk", + "tableFrom": "org_usage_allowance_windows", + "tableTo": "org_usage_allowance_entitlements", + "columnsFrom": [ + "entitlement_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "org_usage_allowance_windows_created_by_run_id_agent_runs_id_fk": { + "name": "org_usage_allowance_windows_created_by_run_id_agent_runs_id_fk", + "tableFrom": "org_usage_allowance_windows", + "tableTo": "agent_runs", + "columnsFrom": [ + "created_by_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chk_org_usage_allowance_windows_kind": { + "name": "chk_org_usage_allowance_windows_kind", + "value": "\"org_usage_allowance_windows\".\"kind\" IN ('short', 'weekly')" + }, + "chk_org_usage_allowance_windows_limit": { + "name": "chk_org_usage_allowance_windows_limit", + "value": "\"org_usage_allowance_windows\".\"unit_limit\" > 0" + }, + "chk_org_usage_allowance_windows_consumed": { + "name": "chk_org_usage_allowance_windows_consumed", + "value": "\"org_usage_allowance_windows\".\"consumed_units\" >= 0" + }, + "chk_org_usage_allowance_windows_time": { + "name": "chk_org_usage_allowance_windows_time", + "value": "\"org_usage_allowance_windows\".\"expires_at\" > \"org_usage_allowance_windows\".\"starts_at\"" + } + }, + "isRLSEnabled": false + }, + "public.usage_allowance_allocations": { + "name": "usage_allowance_allocations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "usage_event_id": { + "name": "usage_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "short_window_id": { + "name": "short_window_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "weekly_window_id": { + "name": "weekly_window_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "units_applied": { + "name": "units_applied", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "uq_usage_allowance_allocations_usage_event": { + "name": "uq_usage_allowance_allocations_usage_event", + "columns": [ + { + "expression": "usage_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_allowance_allocations_org": { + "name": "idx_usage_allowance_allocations_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_allowance_allocations_run": { + "name": "idx_usage_allowance_allocations_run", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "usage_allowance_allocations_usage_event_id_usage_event_id_fk": { + "name": "usage_allowance_allocations_usage_event_id_usage_event_id_fk", + "tableFrom": "usage_allowance_allocations", + "tableTo": "usage_event", + "columnsFrom": [ + "usage_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "usage_allowance_allocations_run_id_agent_runs_id_fk": { + "name": "usage_allowance_allocations_run_id_agent_runs_id_fk", + "tableFrom": "usage_allowance_allocations", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "usage_allowance_allocations_short_window_id_org_usage_allowance_windows_id_fk": { + "name": "usage_allowance_allocations_short_window_id_org_usage_allowance_windows_id_fk", + "tableFrom": "usage_allowance_allocations", + "tableTo": "org_usage_allowance_windows", + "columnsFrom": [ + "short_window_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "usage_allowance_allocations_weekly_window_id_org_usage_allowance_windows_id_fk": { + "name": "usage_allowance_allocations_weekly_window_id_org_usage_allowance_windows_id_fk", + "tableFrom": "usage_allowance_allocations", + "tableTo": "org_usage_allowance_windows", + "columnsFrom": [ + "weekly_window_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chk_usage_allowance_allocations_units": { + "name": "chk_usage_allowance_allocations_units", + "value": "\"usage_allowance_allocations\".\"units_applied\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.push_subscriptions": { + "name": "push_subscriptions", + "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 + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "p256dh": { + "name": "p256dh", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth": { + "name": "auth", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_push_subscriptions_user_id": { + "name": "idx_push_subscriptions_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_push_subscriptions_endpoint": { + "name": "idx_push_subscriptions_endpoint", + "columns": [ + { + "expression": "endpoint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.relationship_backfill_jobs": { + "name": "relationship_backfill_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "connector_id": { + "name": "connector_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "query": { + "name": "query", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "next_page_token": { + "name": "next_page_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estimated_total": { + "name": "estimated_total", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "scanned_count": { + "name": "scanned_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enqueued_count": { + "name": "enqueued_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_relationship_backfill_jobs_provider": { + "name": "idx_relationship_backfill_jobs_provider", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_relationship_backfill_jobs_status": { + "name": "idx_relationship_backfill_jobs_status", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.relationship_memory_settings": { + "name": "relationship_memory_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "bootstrap_status": { + "name": "bootstrap_status", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "last_sync_at": { + "name": "last_sync_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()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_relationship_memory_settings_provider": { + "name": "idx_relationship_memory_settings_provider", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_relationship_memory_settings_enabled": { + "name": "idx_relationship_memory_settings_enabled", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.relationship_sync_jobs": { + "name": "relationship_sync_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "run_after_at": { + "name": "run_after_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "locked_at": { + "name": "locked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_relationship_sync_jobs_dedupe": { + "name": "idx_relationship_sync_jobs_dedupe", + "columns": [ + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_relationship_sync_jobs_pending": { + "name": "idx_relationship_sync_jobs_pending", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "priority", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_after_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_relationship_sync_jobs_scope_provider": { + "name": "idx_relationship_sync_jobs_scope_provider", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.run_built_in_admissions": { + "name": "run_built_in_admissions", + "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 + }, + "kind": { + "name": "kind", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_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 + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "idx_run_builtin_admissions_run_status": { + "name": "idx_run_builtin_admissions_run_status", + "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": {} + }, + "idx_run_builtin_admissions_run_created": { + "name": "idx_run_builtin_admissions_run_created", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_run_builtin_admissions_expires_at": { + "name": "idx_run_builtin_admissions_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "run_built_in_admissions_run_id_agent_runs_id_fk": { + "name": "run_built_in_admissions_run_id_agent_runs_id_fk", + "tableFrom": "run_built_in_admissions", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.run_uploaded_files": { + "name": "run_uploaded_files", + "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 + }, + "source": { + "name": "source", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "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": { + "idx_run_uploaded_files_run": { + "name": "idx_run_uploaded_files_run", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_run_uploaded_files_run_source_external": { + "name": "idx_run_uploaded_files_run_source_external", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_run_uploaded_files_source_external": { + "name": "idx_run_uploaded_files_source_external", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "run_uploaded_files_run_id_agent_runs_id_fk": { + "name": "run_uploaded_files_run_id_agent_runs_id_fk", + "tableFrom": "run_uploaded_files", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.runner_job_queue": { + "name": "runner_job_queue", + "schema": "", + "columns": { + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "runner_group": { + "name": "runner_group", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "profile": { + "name": "profile", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "default": "'vm0/default'" + }, + "session_id": { + "name": "session_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "execution_context": { + "name": "execution_context", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "runner_job_queue_group_profile_unclaimed_idx": { + "name": "runner_job_queue_group_profile_unclaimed_idx", + "columns": [ + { + "expression": "runner_group", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "profile", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "claimed_at IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "runner_job_queue_session_id_unclaimed_idx": { + "name": "runner_job_queue_session_id_unclaimed_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "claimed_at IS NULL AND session_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "runner_job_queue_expires_at_idx": { + "name": "runner_job_queue_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "runner_job_queue_run_id_agent_runs_id_fk": { + "name": "runner_job_queue_run_id_agent_runs_id_fk", + "tableFrom": "runner_job_queue", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.runner_state": { + "name": "runner_state", + "schema": "", + "columns": { + "runner_id": { + "name": "runner_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "runner_name": { + "name": "runner_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "runner_group": { + "name": "runner_group", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "total_vcpu": { + "name": "total_vcpu", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_memory_mb": { + "name": "total_memory_mb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_concurrent": { + "name": "max_concurrent", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "allocated_vcpu": { + "name": "allocated_vcpu", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "allocated_memory_mb": { + "name": "allocated_memory_mb", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "running_count": { + "name": "running_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "admittable_profiles": { + "name": "admittable_profiles", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "held_session_states": { + "name": "held_session_states", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "mode": { + "name": "mode", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "runner_state_group_idx": { + "name": "runner_state_group_idx", + "columns": [ + { + "expression": "runner_group", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "runner_state_last_seen_idx": { + "name": "runner_state_last_seen_idx", + "columns": [ + { + "expression": "last_seen_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sandbox_telemetry": { + "name": "sandbox_telemetry", + "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 + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_sandbox_telemetry_run_id": { + "name": "idx_sandbox_telemetry_run_id", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sandbox_telemetry_run_id_agent_runs_id_fk": { + "name": "sandbox_telemetry_run_id_agent_runs_id_fk", + "tableFrom": "sandbox_telemetry", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.secrets": { + "name": "secrets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "encrypted_value": { + "name": "encrypted_value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_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": { + "idx_secrets_type": { + "name": "idx_secrets_type", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_secrets_org": { + "name": "idx_secrets_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_secrets_org_user_name_type": { + "name": "idx_secrets_org_user_name_type", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.skills": { + "name": "skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "full_path": { + "name": "full_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_id": { + "name": "storage_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "version_hash": { + "name": "version_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "commit_sha": { + "name": "commit_sha", + "type": "varchar(40)", + "primaryKey": false, + "notNull": false + }, + "frontmatter": { + "name": "frontmatter", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "s3_key": { + "name": "s3_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "file_count": { + "name": "file_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_skills_name": { + "name": "idx_skills_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_skills_storage_id": { + "name": "idx_skills_storage_id", + "columns": [ + { + "expression": "storage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "skills_storage_id_storages_id_fk": { + "name": "skills_storage_id_storages_id_fk", + "tableFrom": "skills", + "tableTo": "storages", + "columnsFrom": [ + "storage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "skills_url_unique": { + "name": "skills_url_unique", + "nullsNotDistinct": false, + "columns": [ + "url" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_org_connections": { + "name": "slack_org_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slack_user_id": { + "name": "slack_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "slack_workspace_id": { + "name": "slack_workspace_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "vm0_user_id": { + "name": "vm0_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dm_welcome_sent": { + "name": "dm_welcome_sent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_slack_org_connections_user_workspace": { + "name": "idx_slack_org_connections_user_workspace", + "columns": [ + { + "expression": "slack_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slack_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_org_connections_workspace": { + "name": "idx_slack_org_connections_workspace", + "columns": [ + { + "expression": "slack_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_org_connections_vm0_user_workspace": { + "name": "idx_slack_org_connections_vm0_user_workspace", + "columns": [ + { + "expression": "vm0_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slack_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_org_connections_slack_workspace_id_slack_org_installations_slack_workspace_id_fk": { + "name": "slack_org_connections_slack_workspace_id_slack_org_installations_slack_workspace_id_fk", + "tableFrom": "slack_org_connections", + "tableTo": "slack_org_installations", + "columnsFrom": [ + "slack_workspace_id" + ], + "columnsTo": [ + "slack_workspace_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_org_installations": { + "name": "slack_org_installations", + "schema": "", + "columns": { + "slack_workspace_id": { + "name": "slack_workspace_id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "slack_workspace_name": { + "name": "slack_workspace_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "encrypted_bot_token": { + "name": "encrypted_bot_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bot_user_id": { + "name": "bot_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "installed_by_user_id": { + "name": "installed_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bot_scopes": { + "name": "bot_scopes", + "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": { + "idx_slack_org_installations_org": { + "name": "idx_slack_org_installations_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_org_installations_org_unique": { + "name": "idx_slack_org_installations_org_unique", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "org_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_org_thread_sessions": { + "name": "slack_org_thread_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slack_channel_id": { + "name": "slack_channel_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "slack_thread_ts": { + "name": "slack_thread_ts", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "agent_session_id": { + "name": "agent_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "computer_use_host_id": { + "name": "computer_use_host_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_slack_org_thread_sessions_conn_channel_thread": { + "name": "idx_slack_org_thread_sessions_conn_channel_thread", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slack_channel_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slack_thread_ts", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_org_thread_sessions_connection": { + "name": "idx_slack_org_thread_sessions_connection", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_org_thread_sessions_computer_use_host": { + "name": "idx_slack_org_thread_sessions_computer_use_host", + "columns": [ + { + "expression": "computer_use_host_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_org_thread_sessions_connection_id_slack_org_connections_id_fk": { + "name": "slack_org_thread_sessions_connection_id_slack_org_connections_id_fk", + "tableFrom": "slack_org_thread_sessions", + "tableTo": "slack_org_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_org_thread_sessions_agent_session_id_agent_sessions_id_fk": { + "name": "slack_org_thread_sessions_agent_session_id_agent_sessions_id_fk", + "tableFrom": "slack_org_thread_sessions", + "tableTo": "agent_sessions", + "columnsFrom": [ + "agent_session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "slack_org_thread_sessions_computer_use_host_id_computer_use_hosts_id_fk": { + "name": "slack_org_thread_sessions_computer_use_host_id_computer_use_hosts_id_fk", + "tableFrom": "slack_org_thread_sessions", + "tableTo": "computer_use_hosts", + "columnsFrom": [ + "computer_use_host_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack_user_agent_preferences": { + "name": "slack_user_agent_preferences", + "schema": "", + "columns": { + "vm0_user_id": { + "name": "vm0_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selected_compose_id": { + "name": "selected_compose_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "slack_user_agent_preferences_selected_compose_id_agent_composes_id_fk": { + "name": "slack_user_agent_preferences_selected_compose_id_agent_composes_id_fk", + "tableFrom": "slack_user_agent_preferences", + "tableTo": "agent_composes", + "columnsFrom": [ + "selected_compose_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "slack_user_agent_preferences_pkey": { + "name": "slack_user_agent_preferences_pkey", + "columns": [ + "vm0_user_id", + "org_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.storage_version_lineage": { + "name": "storage_version_lineage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "storage_id": { + "name": "storage_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version_id": { + "name": "version_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "parent_version_id": { + "name": "parent_version_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "storage_type": { + "name": "storage_type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_storage_version_lineage_storage_version": { + "name": "idx_storage_version_lineage_storage_version", + "columns": [ + { + "expression": "storage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_storage_version_lineage_storage_parent": { + "name": "idx_storage_version_lineage_storage_parent", + "columns": [ + { + "expression": "storage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_storage_version_lineage_run": { + "name": "idx_storage_version_lineage_run", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "storage_version_lineage_storage_id_storages_id_fk": { + "name": "storage_version_lineage_storage_id_storages_id_fk", + "tableFrom": "storage_version_lineage", + "tableTo": "storages", + "columnsFrom": [ + "storage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "storage_version_lineage_run_id_agent_runs_id_fk": { + "name": "storage_version_lineage_run_id_agent_runs_id_fk", + "tableFrom": "storage_version_lineage", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.storage_versions": { + "name": "storage_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(64)", + "primaryKey": true, + "notNull": true + }, + "storage_id": { + "name": "storage_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "s3_key": { + "name": "s3_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "file_count": { + "name": "file_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "message": { + "name": "message", + "type": "text", + "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()" + } + }, + "indexes": {}, + "foreignKeys": { + "storage_versions_storage_id_storages_id_fk": { + "name": "storage_versions_storage_id_storages_id_fk", + "tableFrom": "storage_versions", + "tableTo": "storages", + "columnsFrom": [ + "storage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.storages": { + "name": "storages", + "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 + }, + "name": { + "name": "name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'volume'" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "s3_prefix": { + "name": "s3_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "file_count": { + "name": "file_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "head_version_id": { + "name": "head_version_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_storages_org": { + "name": "idx_storages_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_storages_org_user_name_type": { + "name": "idx_storages_org_user_name_type", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "storages_head_version_id_storage_versions_id_fk": { + "name": "storages_head_version_id_storage_versions_id_fk", + "tableFrom": "storages", + "tableTo": "storage_versions", + "columnsFrom": [ + "head_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_storage_presigned_url_cache": { + "name": "system_storage_presigned_url_cache", + "schema": "", + "columns": { + "cache_key": { + "name": "cache_key", + "type": "varchar(64)", + "primaryKey": true, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "default": "'system_storage'" + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "object_key": { + "name": "object_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_version_id": { + "name": "storage_version_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "resolved_org_id": { + "name": "resolved_org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_endpoint": { + "name": "public_endpoint", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "ttl_seconds": { + "name": "ttl_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "presigned_url": { + "name": "presigned_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "refresh_after": { + "name": "refresh_after", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "last_requested_at": { + "name": "last_requested_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": { + "idx_system_storage_presigned_url_cache_refresh_after": { + "name": "idx_system_storage_presigned_url_cache_refresh_after", + "columns": [ + { + "expression": "refresh_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_system_storage_presigned_url_cache_last_requested_at": { + "name": "idx_system_storage_presigned_url_cache_last_requested_at", + "columns": [ + { + "expression": "last_requested_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_system_storage_presigned_url_cache_active_refresh": { + "name": "idx_system_storage_presigned_url_cache_active_refresh", + "columns": [ + { + "expression": "last_requested_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "refresh_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_system_storage_presigned_url_cache_scope_refresh_after": { + "name": "idx_system_storage_presigned_url_cache_scope_refresh_after", + "columns": [ + { + "expression": "scope", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "refresh_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_system_storage_presigned_url_cache_scope_active_refresh": { + "name": "idx_system_storage_presigned_url_cache_scope_active_refresh", + "columns": [ + { + "expression": "scope", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_requested_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "refresh_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams_org_connections": { + "name": "teams_org_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "teams_user_id": { + "name": "teams_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "teams_aad_object_id": { + "name": "teams_aad_object_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "teams_tenant_id": { + "name": "teams_tenant_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "vm0_user_id": { + "name": "vm0_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "teams_user_display_name": { + "name": "teams_user_display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "teams_user_principal_name": { + "name": "teams_user_principal_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "dm_welcome_sent": { + "name": "dm_welcome_sent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_teams_org_connections_user_tenant": { + "name": "idx_teams_org_connections_user_tenant", + "columns": [ + { + "expression": "teams_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "teams_tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "teams_user_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_teams_org_connections_aad_tenant": { + "name": "idx_teams_org_connections_aad_tenant", + "columns": [ + { + "expression": "teams_aad_object_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "teams_tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "teams_aad_object_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_teams_org_connections_vm0_tenant": { + "name": "idx_teams_org_connections_vm0_tenant", + "columns": [ + { + "expression": "vm0_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "teams_tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_teams_org_connections_tenant": { + "name": "idx_teams_org_connections_tenant", + "columns": [ + { + "expression": "teams_tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "teams_org_connections_teams_tenant_id_teams_org_installations_teams_tenant_id_fk": { + "name": "teams_org_connections_teams_tenant_id_teams_org_installations_teams_tenant_id_fk", + "tableFrom": "teams_org_connections", + "tableTo": "teams_org_installations", + "columnsFrom": [ + "teams_tenant_id" + ], + "columnsTo": [ + "teams_tenant_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams_org_installations": { + "name": "teams_org_installations", + "schema": "", + "columns": { + "teams_tenant_id": { + "name": "teams_tenant_id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "teams_tenant_name": { + "name": "teams_tenant_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "teams_team_id": { + "name": "teams_team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "teams_team_name": { + "name": "teams_team_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "teams_app_id": { + "name": "teams_app_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "bot_id": { + "name": "bot_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "bot_name": { + "name": "bot_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "service_url": { + "name": "service_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "installed_by_user_id": { + "name": "installed_by_user_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": { + "idx_teams_org_installations_org": { + "name": "idx_teams_org_installations_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_teams_org_installations_org_unique": { + "name": "idx_teams_org_installations_org_unique", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "org_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams_org_thread_sessions": { + "name": "teams_org_thread_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "connection_id": { + "name": "connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "teams_conversation_id": { + "name": "teams_conversation_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "teams_channel_id": { + "name": "teams_channel_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "teams_thread_id": { + "name": "teams_thread_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "agent_session_id": { + "name": "agent_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "computer_use_host_id": { + "name": "computer_use_host_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_teams_org_thread_sessions_conn_conversation_thread": { + "name": "idx_teams_org_thread_sessions_conn_conversation_thread", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "teams_conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "teams_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_teams_org_thread_sessions_connection": { + "name": "idx_teams_org_thread_sessions_connection", + "columns": [ + { + "expression": "connection_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_teams_org_thread_sessions_computer_use_host": { + "name": "idx_teams_org_thread_sessions_computer_use_host", + "columns": [ + { + "expression": "computer_use_host_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "teams_org_thread_sessions_connection_id_teams_org_connections_id_fk": { + "name": "teams_org_thread_sessions_connection_id_teams_org_connections_id_fk", + "tableFrom": "teams_org_thread_sessions", + "tableTo": "teams_org_connections", + "columnsFrom": [ + "connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "teams_org_thread_sessions_agent_session_id_agent_sessions_id_fk": { + "name": "teams_org_thread_sessions_agent_session_id_agent_sessions_id_fk", + "tableFrom": "teams_org_thread_sessions", + "tableTo": "agent_sessions", + "columnsFrom": [ + "agent_session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "teams_org_thread_sessions_computer_use_host_id_computer_use_hosts_id_fk": { + "name": "teams_org_thread_sessions_computer_use_host_id_computer_use_hosts_id_fk", + "tableFrom": "teams_org_thread_sessions", + "tableTo": "computer_use_hosts", + "columnsFrom": [ + "computer_use_host_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams_user_agent_preferences": { + "name": "teams_user_agent_preferences", + "schema": "", + "columns": { + "vm0_user_id": { + "name": "vm0_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selected_compose_id": { + "name": "selected_compose_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "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": { + "teams_user_agent_preferences_selected_compose_id_agent_composes_id_fk": { + "name": "teams_user_agent_preferences_selected_compose_id_agent_composes_id_fk", + "tableFrom": "teams_user_agent_preferences", + "tableTo": "agent_composes", + "columnsFrom": [ + "selected_compose_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "teams_user_agent_preferences_pkey": { + "name": "teams_user_agent_preferences_pkey", + "columns": [ + "vm0_user_id", + "org_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram_installations": { + "name": "telegram_installations", + "schema": "", + "columns": { + "telegram_bot_id": { + "name": "telegram_bot_id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "bot_username": { + "name": "bot_username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "encrypted_bot_token": { + "name": "encrypted_bot_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "webhook_secret": { + "name": "webhook_secret", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "default_compose_id": { + "name": "default_compose_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_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": { + "idx_telegram_installations_owner": { + "name": "idx_telegram_installations_owner", + "columns": [ + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_installations_org": { + "name": "idx_telegram_installations_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "telegram_installations_default_compose_id_agent_composes_id_fk": { + "name": "telegram_installations_default_compose_id_agent_composes_id_fk", + "tableFrom": "telegram_installations", + "tableTo": "agent_composes", + "columnsFrom": [ + "default_compose_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram_messages": { + "name": "telegram_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "installation_id": { + "name": "installation_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "official_org_id": { + "name": "official_org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "official_user_link_id": { + "name": "official_user_link_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "chat_id": { + "name": "chat_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "from_user_id": { + "name": "from_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "from_username": { + "name": "from_username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "from_display_name": { + "name": "from_display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_id": { + "name": "file_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "file_type": { + "name": "file_type", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_mime_type": { + "name": "file_mime_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "file_width": { + "name": "file_width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "file_height": { + "name": "file_height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "file_duration": { + "name": "file_duration", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "entities": { + "name": "entities", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_telegram_messages_unique": { + "name": "idx_telegram_messages_unique", + "columns": [ + { + "expression": "installation_id", + "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": true, + "where": "installation_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_messages_official_unique": { + "name": "idx_telegram_messages_official_unique", + "columns": [ + { + "expression": "official_org_id", + "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": true, + "where": "official_org_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_messages_chat": { + "name": "idx_telegram_messages_chat", + "columns": [ + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "installation_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_messages_official_chat": { + "name": "idx_telegram_messages_official_chat", + "columns": [ + { + "expression": "official_org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "official_org_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_messages_created_at": { + "name": "idx_telegram_messages_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "telegram_messages_installation_id_telegram_installations_telegram_bot_id_fk": { + "name": "telegram_messages_installation_id_telegram_installations_telegram_bot_id_fk", + "tableFrom": "telegram_messages", + "tableTo": "telegram_installations", + "columnsFrom": [ + "installation_id" + ], + "columnsTo": [ + "telegram_bot_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "telegram_messages_official_user_link_id_telegram_official_user_links_id_fk": { + "name": "telegram_messages_official_user_link_id_telegram_official_user_links_id_fk", + "tableFrom": "telegram_messages", + "tableTo": "telegram_official_user_links", + "columnsFrom": [ + "official_user_link_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chk_telegram_messages_one_owner": { + "name": "chk_telegram_messages_one_owner", + "value": "(installation_id IS NOT NULL) <> (official_org_id IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.telegram_official_user_links": { + "name": "telegram_official_user_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "telegram_user_id": { + "name": "telegram_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "telegram_username": { + "name": "telegram_username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "telegram_display_name": { + "name": "telegram_display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "vm0_user_id": { + "name": "vm0_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dm_welcome_sent": { + "name": "dm_welcome_sent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_telegram_official_user_links_tg_user": { + "name": "idx_telegram_official_user_links_tg_user", + "columns": [ + { + "expression": "telegram_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_official_user_links_vm0_org": { + "name": "idx_telegram_official_user_links_vm0_org", + "columns": [ + { + "expression": "vm0_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_official_user_links_org": { + "name": "idx_telegram_official_user_links_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram_thread_sessions": { + "name": "telegram_thread_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "telegram_user_link_id": { + "name": "telegram_user_link_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "telegram_official_user_link_id": { + "name": "telegram_official_user_link_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "chat_id": { + "name": "chat_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "root_message_id": { + "name": "root_message_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "agent_session_id": { + "name": "agent_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "last_processed_message_id": { + "name": "last_processed_message_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_telegram_thread_sessions_chat_user_link": { + "name": "idx_telegram_thread_sessions_chat_user_link", + "columns": [ + { + "expression": "telegram_user_link_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "root_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "telegram_user_link_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_thread_sessions_chat_official_link": { + "name": "idx_telegram_thread_sessions_chat_official_link", + "columns": [ + { + "expression": "telegram_official_user_link_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "root_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "telegram_official_user_link_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_thread_sessions_user_link": { + "name": "idx_telegram_thread_sessions_user_link", + "columns": [ + { + "expression": "telegram_user_link_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "telegram_user_link_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_thread_sessions_official_user_link": { + "name": "idx_telegram_thread_sessions_official_user_link", + "columns": [ + { + "expression": "telegram_official_user_link_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "telegram_official_user_link_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "telegram_thread_sessions_telegram_user_link_id_telegram_user_links_id_fk": { + "name": "telegram_thread_sessions_telegram_user_link_id_telegram_user_links_id_fk", + "tableFrom": "telegram_thread_sessions", + "tableTo": "telegram_user_links", + "columnsFrom": [ + "telegram_user_link_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "telegram_thread_sessions_telegram_official_user_link_id_telegram_official_user_links_id_fk": { + "name": "telegram_thread_sessions_telegram_official_user_link_id_telegram_official_user_links_id_fk", + "tableFrom": "telegram_thread_sessions", + "tableTo": "telegram_official_user_links", + "columnsFrom": [ + "telegram_official_user_link_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "telegram_thread_sessions_agent_session_id_agent_sessions_id_fk": { + "name": "telegram_thread_sessions_agent_session_id_agent_sessions_id_fk", + "tableFrom": "telegram_thread_sessions", + "tableTo": "agent_sessions", + "columnsFrom": [ + "agent_session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chk_telegram_thread_sessions_one_owner": { + "name": "chk_telegram_thread_sessions_one_owner", + "value": "(telegram_user_link_id IS NOT NULL) <> (telegram_official_user_link_id IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.telegram_user_agent_preferences": { + "name": "telegram_user_agent_preferences", + "schema": "", + "columns": { + "vm0_user_id": { + "name": "vm0_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selected_compose_id": { + "name": "selected_compose_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "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": { + "telegram_user_agent_preferences_selected_compose_id_agent_composes_id_fk": { + "name": "telegram_user_agent_preferences_selected_compose_id_agent_composes_id_fk", + "tableFrom": "telegram_user_agent_preferences", + "tableTo": "agent_composes", + "columnsFrom": [ + "selected_compose_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "telegram_user_agent_preferences_pkey": { + "name": "telegram_user_agent_preferences_pkey", + "columns": [ + "vm0_user_id", + "org_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram_user_links": { + "name": "telegram_user_links", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "telegram_user_id": { + "name": "telegram_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "telegram_username": { + "name": "telegram_username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "telegram_display_name": { + "name": "telegram_display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "installation_id": { + "name": "installation_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "vm0_user_id": { + "name": "vm0_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dm_welcome_sent": { + "name": "dm_welcome_sent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_telegram_user_links_user_installation": { + "name": "idx_telegram_user_links_user_installation", + "columns": [ + { + "expression": "telegram_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_telegram_user_links_vm0_installation": { + "name": "idx_telegram_user_links_vm0_installation", + "columns": [ + { + "expression": "vm0_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "telegram_user_links_installation_id_telegram_installations_telegram_bot_id_fk": { + "name": "telegram_user_links_installation_id_telegram_installations_telegram_bot_id_fk", + "tableFrom": "telegram_user_links", + "tableTo": "telegram_installations", + "columnsFrom": [ + "installation_id" + ], + "columnsTo": [ + "telegram_bot_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.thread_goals": { + "name": "thread_goals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_thread_id": { + "name": "chat_thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "objective": { + "name": "objective", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "objective_brief": { + "name": "objective_brief", + "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": { + "idx_thread_goals_chat_thread_unique": { + "name": "idx_thread_goals_chat_thread_unique", + "columns": [ + { + "expression": "chat_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_thread_goals_org_owner": { + "name": "idx_thread_goals_org_owner", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_thread_goals_org_status": { + "name": "idx_thread_goals_org_status", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "thread_goals_agent_id_zero_agents_id_fk": { + "name": "thread_goals_agent_id_zero_agents_id_fk", + "tableFrom": "thread_goals", + "tableTo": "zero_agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "thread_goals_chat_thread_id_chat_threads_id_fk": { + "name": "thread_goals_chat_thread_id_chat_threads_id_fk", + "tableFrom": "thread_goals", + "tableTo": "chat_threads", + "columnsFrom": [ + "chat_thread_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "thread_goals_status_check": { + "name": "thread_goals_status_check", + "value": "status IN ('active', 'paused', 'blocked', 'complete')" + } + }, + "isRLSEnabled": false + }, + "public.usage_daily": { + "name": "usage_daily", + "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 + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "run_count": { + "name": "run_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "run_time_ms": { + "name": "run_time_ms", + "type": "bigint", + "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": { + "uq_usage_daily_user_org_date": { + "name": "uq_usage_daily_user_org_date", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usage_event": { + "name": "usage_event", + "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": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "credits_charged": { + "name": "credits_charged", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "billing_error": { + "name": "billing_error", + "type": "varchar(50)", + "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": { + "uq_usage_event_idempotency_key": { + "name": "uq_usage_event_idempotency_key", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_event_run_id": { + "name": "idx_usage_event_run_id", + "columns": [ + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_event_org_status": { + "name": "idx_usage_event_org_status", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_event_org_created": { + "name": "idx_usage_event_org_created", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_event_model_created": { + "name": "idx_usage_event_model_created", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_event\".\"kind\" = 'model'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_event_org_user_status_processed": { + "name": "idx_usage_event_org_user_status_processed", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "processed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_usage_event_processed_org_user": { + "name": "idx_usage_event_processed_org_user", + "columns": [ + { + "expression": "processed_at", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"usage_event\".\"status\" = 'processed' AND \"usage_event\".\"processed_at\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "usage_event_run_id_agent_runs_id_fk": { + "name": "usage_event_run_id_agent_runs_id_fk", + "tableFrom": "usage_event", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.usage_pricing": { + "name": "usage_pricing", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "kind": { + "name": "kind", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "unit_price": { + "name": "unit_price", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "unit_size": { + "name": "unit_size", + "type": "bigint", + "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": { + "uq_usage_pricing_kind_provider_category": { + "name": "uq_usage_pricing_kind_provider_category", + "columns": [ + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_behavior_count": { + "name": "user_behavior_count", + "schema": "", + "columns": { + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "behavior_key": { + "name": "behavior_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "first_at": { + "name": "first_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_at": { + "name": "last_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_behavior_count_org_id_user_id_behavior_key_pk": { + "name": "user_behavior_count_org_id_user_id_behavior_key_pk", + "columns": [ + "org_id", + "user_id", + "behavior_key" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_cache": { + "name": "user_cache", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_list_cached_at": { + "name": "org_list_cached_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cached_at": { + "name": "cached_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_connectors": { + "name": "user_connectors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "connector_type": { + "name": "connector_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_user_connectors_unique": { + "name": "idx_user_connectors_unique", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connector_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_connectors_agent_user": { + "name": "idx_user_connectors_agent_user", + "columns": [ + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_connectors_agent_id_zero_agents_id_fk": { + "name": "user_connectors_agent_id_zero_agents_id_fk", + "tableFrom": "user_connectors", + "tableTo": "zero_agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_custom_connectors": { + "name": "user_custom_connectors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "custom_connector_id": { + "name": "custom_connector_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_user_custom_connectors_unique": { + "name": "idx_user_custom_connectors_unique", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "custom_connector_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_custom_connectors_agent_user": { + "name": "idx_user_custom_connectors_agent_user", + "columns": [ + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_custom_connectors_agent_id_zero_agents_id_fk": { + "name": "user_custom_connectors_agent_id_zero_agents_id_fk", + "tableFrom": "user_custom_connectors", + "tableTo": "zero_agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_custom_connectors_custom_connector_id_org_custom_connectors_id_fk": { + "name": "user_custom_connectors_custom_connector_id_org_custom_connectors_id_fk", + "tableFrom": "user_custom_connectors", + "tableTo": "org_custom_connectors", + "columnsFrom": [ + "custom_connector_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_feature_switches": { + "name": "user_feature_switches", + "schema": "", + "columns": { + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "switches": { + "name": "switches", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_feature_switches_org_id_user_id_pk": { + "name": "user_feature_switches_org_id_user_id_pk", + "columns": [ + "org_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_permission_grants": { + "name": "user_permission_grants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "connector_ref": { + "name": "connector_ref", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "varchar(8)", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "uq_user_permission_grants_grant": { + "name": "uq_user_permission_grants_grant", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "connector_ref", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_permission_grants_lookup": { + "name": "idx_user_permission_grants_lookup", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_permission_grants_user_id": { + "name": "idx_user_permission_grants_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_user_permission_grants_agent_id": { + "name": "idx_user_permission_grants_agent_id", + "columns": [ + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_permission_grants_agent_id_zero_agents_id_fk": { + "name": "user_permission_grants_agent_id_zero_agents_id_fk", + "tableFrom": "user_permission_grants", + "tableTo": "zero_agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "chk_user_permission_grants_action": { + "name": "chk_user_permission_grants_action", + "value": "\"user_permission_grants\".\"action\" IN ('allow', 'deny')" + } + }, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email_unsubscribed": { + "name": "email_unsubscribed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.variables": { + "name": "variables", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_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": { + "idx_variables_org": { + "name": "idx_variables_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_variables_org_user_type_name": { + "name": "idx_variables_org_user_type_name", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.vm0_api_keys": { + "name": "vm0_api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "vendor": { + "name": "vendor", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "api_key": { + "name": "api_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "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": { + "idx_vm0_api_keys_vendor": { + "name": "idx_vm0_api_keys_vendor", + "columns": [ + { + "expression": "vendor", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.zero_agent_drafts": { + "name": "zero_agent_drafts", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "draft_content": { + "name": "draft_content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_attachments": { + "name": "draft_attachments", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_zero_agent_drafts_user_org_agent": { + "name": "idx_zero_agent_drafts_user_org_agent", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "zero_agent_drafts_agent_id_zero_agents_id_fk": { + "name": "zero_agent_drafts_agent_id_zero_agents_id_fk", + "tableFrom": "zero_agent_drafts", + "tableTo": "zero_agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.zero_agents": { + "name": "zero_agents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'public'" + }, + "display_name": { + "name": "display_name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sound": { + "name": "sound", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": false + }, + "model_provider_id": { + "name": "model_provider_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "selected_model": { + "name": "selected_model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "prefer_personal_provider": { + "name": "prefer_personal_provider", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_zero_agents_org_name": { + "name": "idx_zero_agents_org_name", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_agents_org": { + "name": "idx_zero_agents_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "zero_agents_id_agent_composes_id_fk": { + "name": "zero_agents_id_agent_composes_id_fk", + "tableFrom": "zero_agents", + "tableTo": "agent_composes", + "columnsFrom": [ + "id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "zero_agents_model_provider_id_model_providers_id_fk": { + "name": "zero_agents_model_provider_id_model_providers_id_fk", + "tableFrom": "zero_agents", + "tableTo": "model_providers", + "columnsFrom": [ + "model_provider_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.zero_runs": { + "name": "zero_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "trigger_source": { + "name": "trigger_source", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "workflow_trigger_id": { + "name": "workflow_trigger_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "run_group_id": { + "name": "run_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "goal_id": { + "name": "goal_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "trigger_agent_id": { + "name": "trigger_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "model_provider": { + "name": "model_provider", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "model_provider_id": { + "name": "model_provider_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "model_provider_credential_scope": { + "name": "model_provider_credential_scope", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "selected_model": { + "name": "selected_model", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "chat_thread_id": { + "name": "chat_thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trigger_brief": { + "name": "trigger_brief", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_zero_runs_chat_thread_id": { + "name": "idx_zero_runs_chat_thread_id", + "columns": [ + { + "expression": "chat_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "chat_thread_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_runs_workflow_trigger": { + "name": "idx_zero_runs_workflow_trigger", + "columns": [ + { + "expression": "workflow_trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "workflow_trigger_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_runs_run_group": { + "name": "idx_zero_runs_run_group", + "columns": [ + { + "expression": "run_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "run_group_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_runs_goal": { + "name": "idx_zero_runs_goal", + "columns": [ + { + "expression": "goal_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "goal_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "zero_runs_id_agent_runs_id_fk": { + "name": "zero_runs_id_agent_runs_id_fk", + "tableFrom": "zero_runs", + "tableTo": "agent_runs", + "columnsFrom": [ + "id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "zero_runs_workflow_trigger_id_zero_workflow_triggers_id_fk": { + "name": "zero_runs_workflow_trigger_id_zero_workflow_triggers_id_fk", + "tableFrom": "zero_runs", + "tableTo": "zero_workflow_triggers", + "columnsFrom": [ + "workflow_trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "zero_runs_goal_id_thread_goals_id_fk": { + "name": "zero_runs_goal_id_thread_goals_id_fk", + "tableFrom": "zero_runs", + "tableTo": "thread_goals", + "columnsFrom": [ + "goal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "zero_runs_trigger_agent_id_agent_composes_id_fk": { + "name": "zero_runs_trigger_agent_id_agent_composes_id_fk", + "tableFrom": "zero_runs", + "tableTo": "agent_composes", + "columnsFrom": [ + "trigger_agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "zero_runs_chat_thread_id_chat_threads_id_fk": { + "name": "zero_runs_chat_thread_id_chat_threads_id_fk", + "tableFrom": "zero_runs", + "tableTo": "chat_threads", + "columnsFrom": [ + "chat_thread_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_user_trigger_threads": { + "name": "workflow_user_trigger_threads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_thread_id": { + "name": "chat_thread_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_workflow_user_trigger_threads_unique": { + "name": "idx_workflow_user_trigger_threads_unique", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_workflow_user_trigger_threads_chat_thread": { + "name": "idx_workflow_user_trigger_threads_chat_thread", + "columns": [ + { + "expression": "chat_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_workflow_user_trigger_threads_workflow_user": { + "name": "idx_workflow_user_trigger_threads_workflow_user", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_user_trigger_threads_workflow_id_zero_workflows_id_fk": { + "name": "workflow_user_trigger_threads_workflow_id_zero_workflows_id_fk", + "tableFrom": "workflow_user_trigger_threads", + "tableTo": "zero_workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_user_trigger_threads_chat_thread_id_chat_threads_id_fk": { + "name": "workflow_user_trigger_threads_chat_thread_id_chat_threads_id_fk", + "tableFrom": "workflow_user_trigger_threads", + "tableTo": "chat_threads", + "columnsFrom": [ + "chat_thread_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.zero_workflow_github_processed_events": { + "name": "zero_workflow_github_processed_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "trigger_id": { + "name": "trigger_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "github_delivery_id": { + "name": "github_delivery_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "repo": { + "name": "repo", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "subject_type": { + "name": "subject_type", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "subject_number": { + "name": "subject_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "label_name_normalized": { + "name": "label_name_normalized", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_zero_workflow_github_processed_delivery": { + "name": "idx_zero_workflow_github_processed_delivery", + "columns": [ + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "github_delivery_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_workflow_github_processed_subject": { + "name": "idx_zero_workflow_github_processed_subject", + "columns": [ + { + "expression": "repo", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "zero_workflow_github_processed_events_trigger_id_zero_workflow_triggers_id_fk": { + "name": "zero_workflow_github_processed_events_trigger_id_zero_workflow_triggers_id_fk", + "tableFrom": "zero_workflow_github_processed_events", + "tableTo": "zero_workflow_triggers", + "columnsFrom": [ + "trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.zero_workflow_triggers": { + "name": "zero_workflow_triggers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'schedule'" + }, + "event_type": { + "name": "event_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "event_config": { + "name": "event_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "schedule_type": { + "name": "schedule_type", + "type": "varchar(16)", + "primaryKey": false, + "notNull": false + }, + "cron_expression": { + "name": "cron_expression", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "interval_seconds": { + "name": "interval_seconds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "at_time": { + "name": "at_time", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "timezone": { + "name": "timezone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "default": "'UTC'" + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_run_id": { + "name": "last_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "consecutive_failures": { + "name": "consecutive_failures", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_zero_workflow_triggers_workflow": { + "name": "idx_zero_workflow_triggers_workflow", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_workflow_triggers_org": { + "name": "idx_zero_workflow_triggers_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_workflow_triggers_next_run": { + "name": "idx_zero_workflow_triggers_next_run", + "columns": [ + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "enabled = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "zero_workflow_triggers_workflow_id_zero_workflows_id_fk": { + "name": "zero_workflow_triggers_workflow_id_zero_workflows_id_fk", + "tableFrom": "zero_workflow_triggers", + "tableTo": "zero_workflows", + "columnsFrom": [ + "workflow_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "zero_workflow_triggers_schedule_config_check": { + "name": "zero_workflow_triggers_schedule_config_check", + "value": "(\n kind = 'schedule'\n AND event_type IS NULL\n AND event_config IS NULL\n AND (\n (schedule_type = 'cron' AND cron_expression IS NOT NULL AND interval_seconds IS NULL AND at_time IS NULL)\n OR (schedule_type = 'loop' AND interval_seconds IS NOT NULL AND cron_expression IS NULL AND at_time IS NULL)\n OR (schedule_type = 'once' AND at_time IS NOT NULL AND cron_expression IS NULL AND interval_seconds IS NULL)\n )\n )\n OR (\n kind = 'event'\n AND event_type IN ('gmail-new-message', 'gmail-label-applied', 'github-label-applied', 'google-calendar-event-created', 'google-calendar-event-updated', 'google-calendar-event-cancelled', 'google-meet-transcript-generated', 'notion-child-page-created', 'notion-database-item-created', 'notion-page-content-updated', 'webhook-received')\n AND event_config IS NOT NULL\n AND schedule_type IS NULL\n AND cron_expression IS NULL\n AND interval_seconds IS NULL\n AND at_time IS NULL\n )" + } + }, + "isRLSEnabled": false + }, + "public.zero_workflow_webhook_deliveries": { + "name": "zero_workflow_webhook_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "trigger_id": { + "name": "trigger_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "delivery_key": { + "name": "delivery_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_sha256": { + "name": "body_sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "received_at": { + "name": "received_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_zero_workflow_webhook_deliveries_key": { + "name": "idx_zero_workflow_webhook_deliveries_key", + "columns": [ + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "delivery_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_workflow_webhook_deliveries_received": { + "name": "idx_zero_workflow_webhook_deliveries_received", + "columns": [ + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "received_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "zero_workflow_webhook_deliveries_trigger_id_zero_workflow_triggers_id_fk": { + "name": "zero_workflow_webhook_deliveries_trigger_id_zero_workflow_triggers_id_fk", + "tableFrom": "zero_workflow_webhook_deliveries", + "tableTo": "zero_workflow_triggers", + "columnsFrom": [ + "trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.zero_workflow_webhook_triggers": { + "name": "zero_workflow_webhook_triggers", + "schema": "", + "columns": { + "trigger_id": { + "name": "trigger_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_token": { + "name": "encrypted_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_secret": { + "name": "encrypted_secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_last_four": { + "name": "secret_last_four", + "type": "varchar(4)", + "primaryKey": false, + "notNull": true + }, + "last_received_at": { + "name": "last_received_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_zero_workflow_webhook_triggers_token_hash": { + "name": "idx_zero_workflow_webhook_triggers_token_hash", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "zero_workflow_webhook_triggers_trigger_id_zero_workflow_triggers_id_fk": { + "name": "zero_workflow_webhook_triggers_trigger_id_zero_workflow_triggers_id_fk", + "tableFrom": "zero_workflow_webhook_triggers", + "tableTo": "zero_workflow_triggers", + "columnsFrom": [ + "trigger_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.zero_workflows": { + "name": "zero_workflows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "instruction": { + "name": "instruction", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_user_id": { + "name": "owner_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_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": { + "idx_zero_workflows_agent": { + "name": "idx_zero_workflows_agent", + "columns": [ + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_workflows_public_agent_name_unique": { + "name": "idx_zero_workflows_public_agent_name_unique", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "visibility = 'public'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_workflows_private_owner_agent_name_unique": { + "name": "idx_zero_workflows_private_owner_agent_name_unique", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "visibility = 'private'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_workflows_org": { + "name": "idx_zero_workflows_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_zero_workflows_org_owner": { + "name": "idx_zero_workflows_org_owner", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "zero_workflows_agent_id_zero_agents_id_fk": { + "name": "zero_workflows_agent_id_zero_agents_id_fk", + "tableFrom": "zero_workflows", + "tableTo": "zero_agents", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.chat_thread_event_kind": { + "name": "chat_thread_event_kind", + "schema": "public", + "values": [ + "created", + "renamed", + "deleted", + "pinned", + "unpinned", + "model_selection_updated", + "sort_touched" + ] + }, + "public.connector_external_code_session_status": { + "name": "connector_external_code_session_status", + "schema": "public", + "values": [ + "pending", + "completing", + "complete", + "expired", + "error" + ] + }, + "public.connector_oauth_device_authorization_session_status": { + "name": "connector_oauth_device_authorization_session_status", + "schema": "public", + "values": [ + "awaiting_user_authorization", + "polling", + "complete", + "denied", + "expired", + "error" + ] + }, + "public.device_code_status": { + "name": "device_code_status", + "schema": "public", + "values": [ + "pending", + "authenticated", + "approved", + "consumed", + "expired", + "denied" + ] + }, + "public.model_provider_auth_session_status": { + "name": "model_provider_auth_session_status", + "schema": "public", + "values": [ + "initializing", + "awaiting_user_approval", + "completing", + "imported", + "expired", + "cancelled", + "error" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/turbo/packages/db/src/migrations/meta/_journal.json b/turbo/packages/db/src/migrations/meta/_journal.json index dcbf0c9c045..8ba6eeaf41e 100644 --- a/turbo/packages/db/src/migrations/meta/_journal.json +++ b/turbo/packages/db/src/migrations/meta/_journal.json @@ -3956,6 +3956,13 @@ "when": 1783586015281, "tag": "0564_soft_gargoyle", "breakpoints": true + }, + { + "idx": 565, + "version": "7", + "when": 1783591372546, + "tag": "0565_eager_iron_monger", + "breakpoints": true } ] -} +} \ No newline at end of file diff --git a/turbo/packages/db/src/schema/computer-use-host.ts b/turbo/packages/db/src/schema/computer-use-host.ts index ce97ffbacc7..d77979849e9 100644 --- a/turbo/packages/db/src/schema/computer-use-host.ts +++ b/turbo/packages/db/src/schema/computer-use-host.ts @@ -142,6 +142,9 @@ export const computerUseAuthorizationRequests = pgTable( slackConnectionId: uuid("slack_connection_id"), slackChannelId: text("slack_channel_id"), slackThreadTs: text("slack_thread_ts"), + teamsConnectionId: uuid("teams_connection_id"), + teamsConversationId: text("teams_conversation_id"), + teamsThreadId: text("teams_thread_id"), expiresAt: timestamp("expires_at").notNull(), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").defaultNow().notNull(), @@ -159,7 +162,7 @@ export const computerUseAuthorizationRequests = pgTable( index("idx_computer_use_auth_requests_expires").on(table.expiresAt), check( "computer_use_auth_requests_source_check", - sql`source IN ('chat', 'slack')`, + sql`source IN ('chat', 'slack', 'teams')`, ), check( "computer_use_auth_requests_scope_check", @@ -169,12 +172,27 @@ export const computerUseAuthorizationRequests = pgTable( AND slack_connection_id IS NULL AND slack_channel_id IS NULL AND slack_thread_ts IS NULL + AND teams_connection_id IS NULL + AND teams_conversation_id IS NULL + AND teams_thread_id IS NULL ) OR ( source = 'slack' AND chat_thread_id IS NULL AND slack_connection_id IS NOT NULL AND slack_channel_id IS NOT NULL AND slack_thread_ts IS NOT NULL + AND teams_connection_id IS NULL + AND teams_conversation_id IS NULL + AND teams_thread_id IS NULL + ) OR ( + source = 'teams' + AND chat_thread_id IS NULL + AND slack_connection_id IS NULL + AND slack_channel_id IS NULL + AND slack_thread_ts IS NULL + AND teams_connection_id IS NOT NULL + AND teams_conversation_id IS NOT NULL + AND teams_thread_id IS NOT NULL )`, ), ]; diff --git a/turbo/packages/db/src/schema/teams-org-thread-session.ts b/turbo/packages/db/src/schema/teams-org-thread-session.ts index df9b25297ae..ef3dfa93ad5 100644 --- a/turbo/packages/db/src/schema/teams-org-thread-session.ts +++ b/turbo/packages/db/src/schema/teams-org-thread-session.ts @@ -8,6 +8,7 @@ import { } from "drizzle-orm/pg-core"; import { teamsOrgConnections } from "./teams-org-connection"; import { agentSessions } from "./agent-session"; +import { computerUseHosts } from "./computer-use-host"; /** * Org-aware Microsoft Teams thread sessions table. @@ -38,6 +39,12 @@ export const teamsOrgThreadSessions = pgTable( }, { onDelete: "set null" }, ), + computerUseHostId: uuid("computer_use_host_id").references( + () => { + return computerUseHosts.id; + }, + { onDelete: "set null" }, + ), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), }, @@ -49,6 +56,9 @@ export const teamsOrgThreadSessions = pgTable( table.teamsThreadId, ), index("idx_teams_org_thread_sessions_connection").on(table.connectionId), + index("idx_teams_org_thread_sessions_computer_use_host").on( + table.computerUseHostId, + ), ]; }, ); From 9a731afdb316696ec0faecd3aef74348ee6bb0ab Mon Sep 17 00:00:00 2001 From: Linghan Hu Date: Thu, 9 Jul 2026 10:11:17 +0000 Subject: [PATCH 3/3] fix: satisfy computer use authorization lint --- .../computer-use-authorization-page.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/turbo/apps/platform/src/views/computer-use-authorization/computer-use-authorization-page.tsx b/turbo/apps/platform/src/views/computer-use-authorization/computer-use-authorization-page.tsx index c9bc0d73e64..98ddacbac5d 100644 --- a/turbo/apps/platform/src/views/computer-use-authorization/computer-use-authorization-page.tsx +++ b/turbo/apps/platform/src/views/computer-use-authorization/computer-use-authorization-page.tsx @@ -40,12 +40,15 @@ function formatTime(value: string): string { function sourceLabel(source: ComputerUseAuthorizationSource): string { switch (source) { - case "chat": + case "chat": { return "this chat thread"; - case "slack": + } + case "slack": { return "this Slack thread"; - case "teams": + } + case "teams": { return "this Teams thread"; + } } }